* [PATCH v7 8/9] cgroup/cpuset: Support multiple source cpusets for cpuset_*attach()
From: Waiman Long @ 2026-06-21 3:28 UTC (permalink / raw)
To: Ridong Chen, Tejun Heo, Johannes Weiner, Michal Koutný,
Li Zefan, Farhad Alemi, Andrew Morton
Cc: cgroups, linux-kernel, Aaron Tomlin, Guopeng Zhang, Gregory Price,
David Hildenbrand, Waiman Long
In-Reply-To: <20260621032816.1806773-1-longman@redhat.com>
There are 2 possible scenarios where the cgroup_taskset structure
passed into the cgroup can_attach() and attach() methods can contain
task migration data with multiple source cpusets.
- A multithread application with threads in different cpusets is
fully migrated into a new cpuset.
- Disabling v2 cpuset controller will move all the tasks in child
cpusets to the parent cpuset.
The current cpuset_can_attach() and cpuset_attach() functions still
expect task migration is from one source cpuset to one destination
cpuset.
Fix that by tracking the set of source (old) cpusets in singly linked
lists with the setting of attach_in_progress flag associated with the
insertion into the list. The list will be iterated when necessary to
properly update the internal data.
To ensure proper DL tasks accounting, the nr_migrate_dl_tasks in both
the source and destination cpusets are decremented/incremented with
their values added to nr_deadline_tasks when the migration is successful.
The setting of the global attach_cpus_updated and attach_mems_updated
flags are also moved from cpuset_attach() to cpuset_can_attach() as the
correct source cpuset can no longer be determined in cpuset_attach()
and cpuset states will not be changed between cpuset_attach() and
cpuset_can_attach() with an earlier patch.
Signed-off-by: Waiman Long <longman@redhat.com>
---
kernel/cgroup/cpuset-internal.h | 1 +
kernel/cgroup/cpuset.c | 66 ++++++++++++++++++++++++++++-----
2 files changed, 57 insertions(+), 10 deletions(-)
diff --git a/kernel/cgroup/cpuset-internal.h b/kernel/cgroup/cpuset-internal.h
index f7aaf01f7cd5..011993b1f756 100644
--- a/kernel/cgroup/cpuset-internal.h
+++ b/kernel/cgroup/cpuset-internal.h
@@ -149,6 +149,7 @@ struct cpuset {
* Tasks are being attached to this cpuset. Used to prevent
* zeroing cpus/mems_allowed between ->can_attach() and ->attach().
*/
+ struct llist_node attach_node;
int attach_in_progress;
/* partition root state */
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 511afb077e2d..c2d172873166 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -37,6 +37,7 @@
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <linux/task_work.h>
+#include <linux/llist.h>
DEFINE_STATIC_KEY_FALSE(cpusets_pre_enable_key);
DEFINE_STATIC_KEY_FALSE(cpusets_enabled_key);
@@ -584,6 +585,7 @@ static struct cpuset *dup_or_alloc_cpuset(struct cpuset *cs)
return NULL;
trial->dl_bw_cpu = -1;
+ init_llist_node(&trial->attach_node);
/* Setup cpumask pointer array */
cpumask_var_t *pmask[4] = {
@@ -2983,9 +2985,10 @@ static int update_prstate(struct cpuset *cs, int new_prs)
* Protected by cpuset_mutex
*
* The attach_cpus_updated/attach_mems_updated flags are set in either
- * cpuset_attach() or cpuset_fork() and used in cpuset_attach_task().
+ * cpuset_can_attach() or cpuset_fork() and used in cpuset_attach_task().
*/
static struct cpuset *cpuset_attach_old_cs;
+static LLIST_HEAD(src_cs_head);
static bool attach_cpus_updated;
static bool attach_mems_updated;
@@ -3001,6 +3004,8 @@ static bool attach_mems_updated;
static int cpuset_can_attach_check(struct cpuset *cs, struct cpuset *oldcs,
bool *psetsched)
{
+ bool cpus_updated, mems_updated;
+
if (cpumask_empty(cs->effective_cpus) ||
(!is_in_v2_mode() && nodes_empty(cs->mems_allowed)))
return -ENOSPC;
@@ -3008,14 +3013,25 @@ static int cpuset_can_attach_check(struct cpuset *cs, struct cpuset *oldcs,
if (!oldcs)
return 0;
+ if (!llist_on_list(&oldcs->attach_node)) {
+ llist_add(&oldcs->attach_node, &src_cs_head);
+ oldcs->attach_in_progress++;
+ }
+
+ cpus_updated = !cpumask_equal(cs->effective_cpus, oldcs->effective_cpus);
+ mems_updated = !nodes_equal(cs->effective_mems, oldcs->effective_mems);
+
+ if (cpus_updated)
+ attach_cpus_updated = true;
+ if (mems_updated)
+ attach_mems_updated = true;
+
/*
* Skip rights over task setsched check in v2 when nothing changes,
* migration permission derives from hierarchy ownership in
* cgroup_procs_write_permission()).
*/
- *psetsched = !cpuset_v2() ||
- !cpumask_equal(cs->effective_cpus, oldcs->effective_cpus) ||
- !nodes_equal(cs->effective_mems, oldcs->effective_mems);
+ *psetsched = !cpuset_v2() || cpus_updated || mems_updated;
/*
* A v1 cpuset with tasks will have no CPU left only when CPU hotplug
@@ -3056,6 +3072,26 @@ static void reset_migrate_dl_data(struct cpuset *cs)
cs->dl_bw_cpu = -1;
}
+/*
+ * Clear and optionally apply (@cancel is false) the attach related data in the
+ * source cpusets.
+ */
+static void clear_attach_data(struct llist_head *head, bool cancel)
+{
+ struct cpuset *cs, *next;
+ struct llist_node *lnode = __llist_del_all(head);
+
+ llist_for_each_entry_safe(cs, next, lnode, attach_node) {
+ init_llist_node(&cs->attach_node);
+ if (cs->nr_migrate_dl_tasks) {
+ if (!cancel)
+ cs->nr_deadline_tasks += cs->nr_migrate_dl_tasks;
+ cs->nr_migrate_dl_tasks = 0;
+ }
+ dec_attach_in_progress_locked(cs);
+ }
+}
+
/* Called by cgroups to determine if a cpuset is usable; cpuset_mutex held */
static int cpuset_can_attach(struct cgroup_taskset *tset)
{
@@ -3071,6 +3107,8 @@ static int cpuset_can_attach(struct cgroup_taskset *tset)
cs = css_cs(css);
mutex_lock(&cpuset_mutex);
+ attach_cpus_updated = false;
+ attach_mems_updated = false;
/* Check to see if task is allowed in the cpuset */
ret = cpuset_can_attach_check(cs, oldcs, &setsched_check);
@@ -3095,6 +3133,15 @@ static int cpuset_can_attach(struct cgroup_taskset *tset)
* selected as cpuset_attach_old_cs.
*/
cgroup_taskset_for_each(task, css, tset) {
+ struct cpuset *new_oldcs = task_cs(task);
+
+ if (new_oldcs != oldcs) {
+ oldcs = new_oldcs;
+ ret = cpuset_can_attach_check(cs, oldcs, &setsched_check);
+ if (ret)
+ goto out_unlock;
+ }
+
ret = task_can_attach(task);
if (ret)
goto out_unlock;
@@ -3116,6 +3163,7 @@ static int cpuset_can_attach(struct cgroup_taskset *tset)
* contribute to sum_migrate_dl_bw.
*/
cs->nr_migrate_dl_tasks++;
+ oldcs->nr_migrate_dl_tasks--;
if (dl_task_needs_bw_move(task, cs->effective_cpus))
cs->sum_migrate_dl_bw += task->dl.dl_bw;
}
@@ -3126,9 +3174,9 @@ static int cpuset_can_attach(struct cgroup_taskset *tset)
out_unlock:
if (ret) {
reset_migrate_dl_data(cs);
+ clear_attach_data(&src_cs_head, true);
} else {
cs->attach_in_progress++;
- oldcs->attach_in_progress++;
}
mutex_unlock(&cpuset_mutex);
@@ -3145,6 +3193,7 @@ static void cpuset_cancel_attach(struct cgroup_taskset *tset)
mutex_lock(&cpuset_mutex);
dec_attach_in_progress_locked(cs);
+ clear_attach_data(&src_cs_head, true);
if (cs->dl_bw_cpu >= 0)
dl_bw_free(cs->dl_bw_cpu, cs->sum_migrate_dl_bw);
@@ -3224,7 +3273,6 @@ static void cpuset_attach(struct cgroup_taskset *tset)
struct task_struct *task;
struct cgroup_subsys_state *css;
struct cpuset *cs;
- struct cpuset *oldcs = cpuset_attach_old_cs;
cgroup_taskset_first(tset, &css);
cs = css_cs(css);
@@ -3232,9 +3280,6 @@ static void cpuset_attach(struct cgroup_taskset *tset)
lockdep_assert_cpus_held(); /* see cgroup_attach_lock() */
mutex_lock(&cpuset_mutex);
queue_task_work = false;
-
- attach_cpus_updated = !cpumask_equal(cs->effective_cpus, oldcs->effective_cpus);
- attach_mems_updated = !nodes_equal(cs->effective_mems, oldcs->effective_mems);
guarantee_online_mems(cs, &cpuset_attach_nodemask_to);
/*
@@ -3256,10 +3301,10 @@ static void cpuset_attach(struct cgroup_taskset *tset)
if (cs->nr_migrate_dl_tasks) {
cs->nr_deadline_tasks += cs->nr_migrate_dl_tasks;
- oldcs->nr_deadline_tasks -= cs->nr_migrate_dl_tasks;
reset_migrate_dl_data(cs);
}
+ clear_attach_data(&src_cs_head, false);
dec_attach_in_progress_locked(cs);
mutex_unlock(&cpuset_mutex);
@@ -3777,6 +3822,7 @@ int __init cpuset_init(void)
cpumask_setall(top_cpuset.effective_xcpus);
cpumask_setall(top_cpuset.exclusive_cpus);
nodes_setall(top_cpuset.effective_mems);
+ init_llist_node(&top_cpuset.attach_node);
cpuset1_init(&top_cpuset);
--
2.54.0
^ permalink raw reply related
* [PATCH v7 9/9] cgroup/cpuset: Support multiple destination cpusets for cpuset_*attach()
From: Waiman Long @ 2026-06-21 3:28 UTC (permalink / raw)
To: Ridong Chen, Tejun Heo, Johannes Weiner, Michal Koutný,
Li Zefan, Farhad Alemi, Andrew Morton
Cc: cgroups, linux-kernel, Aaron Tomlin, Guopeng Zhang, Gregory Price,
David Hildenbrand, Waiman Long
In-Reply-To: <20260621032816.1806773-1-longman@redhat.com>
The only case where the cgroup_taskset structure requires task migration
to multiple cpusets is when enabling a cpuset controller in cgroup v2
where the newly created child cpusets inherits the same effective CPUs
and memory nodes from the parent. In that case, task migration can happen
directly with no update to tasks' CPU and memory nodes assignment and no
further work needed from the cpuset side exact updating nr_deadline_tasks
when DL tasks are involved and setting old_mems_allowed in the child
cpusets.
Do that by tracking all the destination cpusets with a new dst_cs_head
singly linked list again with the setting of attach_in_progress
associated with the insertion into the list.
It is assumed that a given cpuset cannot be both a source and a
destination cpuset. If such condition happens or when there are multiple
destination cpusets with CPU or memory nodes changes, the current code
will not handle it correctly. So it will print a warning and fail the
attach operation in these unexpected cases as we will have to enhance
the code to support this if such use cases are valid and not coding bugs.
Signed-off-by: Waiman Long <longman@redhat.com>
---
kernel/cgroup/cpuset-internal.h | 1 +
kernel/cgroup/cpuset.c | 121 +++++++++++++++++++-------------
2 files changed, 75 insertions(+), 47 deletions(-)
diff --git a/kernel/cgroup/cpuset-internal.h b/kernel/cgroup/cpuset-internal.h
index 011993b1f756..900e74ac3538 100644
--- a/kernel/cgroup/cpuset-internal.h
+++ b/kernel/cgroup/cpuset-internal.h
@@ -151,6 +151,7 @@ struct cpuset {
*/
struct llist_node attach_node;
int attach_in_progress;
+ bool attach_source;
/* partition root state */
int partition_root_state;
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index c2d172873166..aff86acea701 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -2986,11 +2986,16 @@ static int update_prstate(struct cpuset *cs, int new_prs)
*
* The attach_cpus_updated/attach_mems_updated flags are set in either
* cpuset_can_attach() or cpuset_fork() and used in cpuset_attach_task().
+ *
+ * The attach_many_dest_cs is set when there are multiple destination cpusets
+ * for task migration.
*/
static struct cpuset *cpuset_attach_old_cs;
static LLIST_HEAD(src_cs_head);
+static LLIST_HEAD(dst_cs_head);
static bool attach_cpus_updated;
static bool attach_mems_updated;
+static bool attach_many_dest_cs;
/*
* Check to see if a cpuset can accept a new task
@@ -3013,9 +3018,25 @@ static int cpuset_can_attach_check(struct cpuset *cs, struct cpuset *oldcs,
if (!oldcs)
return 0;
+ /*
+ * The same cpuset cannot be both a source and a destination.
+ * The current code does not support that, print a warning and
+ * fail the attach if so.
+ */
+ if (WARN_ON_ONCE((!oldcs->attach_source &&
+ llist_on_list(&oldcs->attach_node)) ||
+ cs->attach_source))
+ return -EINVAL;
+
if (!llist_on_list(&oldcs->attach_node)) {
llist_add(&oldcs->attach_node, &src_cs_head);
oldcs->attach_in_progress++;
+ oldcs->attach_source = true;
+ }
+
+ if (!llist_on_list(&cs->attach_node)) {
+ llist_add(&cs->attach_node, &dst_cs_head);
+ cs->attach_in_progress++;
}
cpus_updated = !cpumask_equal(cs->effective_cpus, oldcs->effective_cpus);
@@ -3046,35 +3067,31 @@ static int cpuset_can_attach_check(struct cpuset *cs, struct cpuset *oldcs,
return 0;
}
-static int cpuset_reserve_dl_bw(struct cpuset *cs)
+static int cpuset_reserve_dl_bw(void)
{
+ struct cpuset *cs;
int cpu, ret;
- if (!cs->sum_migrate_dl_bw)
- return 0;
+ llist_for_each_entry(cs, dst_cs_head.first, attach_node) {
+ if (!cs->sum_migrate_dl_bw)
+ continue;
- cpu = cpumask_any_and(cpu_active_mask, cs->effective_cpus);
- if (unlikely(cpu >= nr_cpu_ids))
- return -EINVAL;
+ cpu = cpumask_any_and(cpu_active_mask, cs->effective_cpus);
+ if (unlikely(cpu >= nr_cpu_ids))
+ return -EINVAL;
- ret = dl_bw_alloc(cpu, cs->sum_migrate_dl_bw);
- if (ret)
- return ret;
+ ret = dl_bw_alloc(cpu, cs->sum_migrate_dl_bw);
+ if (ret)
+ return ret;
- cs->dl_bw_cpu = cpu;
+ cs->dl_bw_cpu = cpu;
+ }
return 0;
}
-static void reset_migrate_dl_data(struct cpuset *cs)
-{
- cs->nr_migrate_dl_tasks = 0;
- cs->sum_migrate_dl_bw = 0;
- cs->dl_bw_cpu = -1;
-}
-
/*
* Clear and optionally apply (@cancel is false) the attach related data in the
- * source cpusets.
+ * source or destination cpuset.
*/
static void clear_attach_data(struct llist_head *head, bool cancel)
{
@@ -3086,9 +3103,14 @@ static void clear_attach_data(struct llist_head *head, bool cancel)
if (cs->nr_migrate_dl_tasks) {
if (!cancel)
cs->nr_deadline_tasks += cs->nr_migrate_dl_tasks;
+ else if (cs->dl_bw_cpu >= 0) /* && cacnel */
+ dl_bw_free(cs->dl_bw_cpu, cs->sum_migrate_dl_bw);
cs->nr_migrate_dl_tasks = 0;
+ cs->sum_migrate_dl_bw = 0;
+ cs->dl_bw_cpu = -1;
}
dec_attach_in_progress_locked(cs);
+ cs->attach_source = false;
}
}
@@ -3109,6 +3131,7 @@ static int cpuset_can_attach(struct cgroup_taskset *tset)
mutex_lock(&cpuset_mutex);
attach_cpus_updated = false;
attach_mems_updated = false;
+ attach_many_dest_cs = false;
/* Check to see if task is allowed in the cpuset */
ret = cpuset_can_attach_check(cs, oldcs, &setsched_check);
@@ -3133,9 +3156,13 @@ static int cpuset_can_attach(struct cgroup_taskset *tset)
* selected as cpuset_attach_old_cs.
*/
cgroup_taskset_for_each(task, css, tset) {
+ struct cpuset *new_cs = css_cs(css);
struct cpuset *new_oldcs = task_cs(task);
- if (new_oldcs != oldcs) {
+ if ((new_oldcs != oldcs) || (new_cs != cs)) {
+ if (new_cs != cs)
+ attach_many_dest_cs = true;
+ cs = new_cs;
oldcs = new_oldcs;
ret = cpuset_can_attach_check(cs, oldcs, &setsched_check);
if (ret)
@@ -3169,14 +3196,28 @@ static int cpuset_can_attach(struct cgroup_taskset *tset)
}
}
- ret = cpuset_reserve_dl_bw(cs);
+ /*
+ * The only case where there are multiple destination cpusets for
+ * task migration is when enabling a v2 cpuset controllers where
+ * tasks will be migrated to multiple child cpusets from a parent
+ * cpuset with the same effective CPUs and memory nodes. IOW,
+ * both attach_cpus_updated and attach_mems_updated should be false.
+ * If not, it is a condition that the current code cannot handled.
+ * Print a warning and abort the attach operation as further code
+ * change will be needed.
+ */
+ if (WARN_ON_ONCE(attach_many_dest_cs && (!cpuset_v2() ||
+ attach_cpus_updated || attach_mems_updated))) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ ret = cpuset_reserve_dl_bw();
out_unlock:
if (ret) {
- reset_migrate_dl_data(cs);
clear_attach_data(&src_cs_head, true);
- } else {
- cs->attach_in_progress++;
+ clear_attach_data(&dst_cs_head, true);
}
mutex_unlock(&cpuset_mutex);
@@ -3185,22 +3226,9 @@ static int cpuset_can_attach(struct cgroup_taskset *tset)
static void cpuset_cancel_attach(struct cgroup_taskset *tset)
{
- struct cgroup_subsys_state *css;
- struct cpuset *cs;
-
- cgroup_taskset_first(tset, &css);
- cs = css_cs(css);
-
mutex_lock(&cpuset_mutex);
- dec_attach_in_progress_locked(cs);
clear_attach_data(&src_cs_head, true);
-
- if (cs->dl_bw_cpu >= 0)
- dl_bw_free(cs->dl_bw_cpu, cs->sum_migrate_dl_bw);
-
- if (cs->nr_migrate_dl_tasks)
- reset_migrate_dl_data(cs);
-
+ clear_attach_data(&dst_cs_head, true);
mutex_unlock(&cpuset_mutex);
}
@@ -3286,26 +3314,25 @@ static void cpuset_attach(struct cgroup_taskset *tset)
* In the default hierarchy, enabling cpuset in the child cgroups
* will trigger a cpuset_attach() call with no change in effective cpus
* and mems. In that case, we can optimize out by skipping the task
- * iteration and update.
+ * iteration and update, but the destination cpuset list is iterated to
+ * set old_mems_allowed.
*/
- if (cpuset_v2() && !attach_cpus_updated && !attach_mems_updated)
+ if (cpuset_v2() && !attach_cpus_updated && !attach_mems_updated) {
+ llist_for_each_entry(cs, dst_cs_head.first, attach_node)
+ cs->old_mems_allowed = cpuset_attach_nodemask_to;
goto out;
+ }
+ /* Task iteration shouldn't happen with attach_many_dest_cs set */
cgroup_taskset_for_each(task, css, tset)
cpuset_attach_task(cs, task);
-out:
if (queue_task_work)
schedule_flush_migrate_mm();
cs->old_mems_allowed = cpuset_attach_nodemask_to;
-
- if (cs->nr_migrate_dl_tasks) {
- cs->nr_deadline_tasks += cs->nr_migrate_dl_tasks;
- reset_migrate_dl_data(cs);
- }
-
+out:
clear_attach_data(&src_cs_head, false);
- dec_attach_in_progress_locked(cs);
+ clear_attach_data(&dst_cs_head, false);
mutex_unlock(&cpuset_mutex);
}
--
2.54.0
^ permalink raw reply related
* [BUG] mm: mglru: stale aging batch triggers lru_gen_exit_memcg warning
From: Peiyang He @ 2026-06-21 13:50 UTC (permalink / raw)
To: akpm, hannes, linux-mm
Cc: mhocko, roman.gushchin, shakeel.butt, muchun.song, qi.zheng,
kasong, baohua, axelrasmussen, yuanchu, weixugc, david, ljs,
cgroups, linux-kernel, syzkaller
[-- Attachment #1: Type: text/plain, Size: 9372 bytes --]
Hello,
I hit the following warning while fuzzing other kernel code with Syzkaller.
The original Syzkaller report:
WARNING: mm/vmscan.c:5867 at lru_gen_exit_memcg+0x26f/0x300
mm/vmscan.c:5867, CPU#0: kworker/0:0/9
Modules linked in:
CPU: 0 UID: 0 PID: 9 Comm: kworker/0:0 Not tainted 7.1.0 #2 PREEMPT(full)
Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix,
1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Workqueue: cgroup_free css_free_rwork_fn
RIP: 0010:lru_gen_exit_memcg+0x26f/0x300 mm/vmscan.c:5867
Code: 89 de e8 d4 62 ba ff 49 83 fd 3f 0f 86 9c fe ff ff 48 83 c4 08 5b
5d 41 5c 41 5d 41 5e 41 5f e9 17 68 ba ff e8 12 68 ba ff 90 <0f> 0b 90
e9 b0 fe ff ff e8 04 68 ba ff 66 90 e8 fd 67 ba ff 90 0f
RSP: 0018:ffffc900001afb78 EFLAGS: 00010293
RAX: 0000000000000000 RBX: 0000000000000000 RCX: ffffffff82049e88
RDX: ffff888016f35c40 RSI: ffffffff8204a02e RDI: ffff88801d4103b8
RBP: dffffc0000000000 R08: 0000000000000005 R09: 0000000000000040
R10: 0000000000000000 R11: 0000000000002ba4 R12: ffff8880481f1600
R13: ffff88801d410650 R14: ffff88801d410040 R15: dead000000000100
FS: 0000000000000000(0000) GS:ffff888098d91000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 000055ac6490c1d8 CR3: 00000000249b0000 CR4: 0000000000350ef0
Call Trace:
<TASK>
mem_cgroup_free mm/memcontrol.c:3972 [inline]
mem_cgroup_css_free+0x76/0xb0 mm/memcontrol.c:4241
css_free_rwork_fn+0x125/0x1260 kernel/cgroup/cgroup.c:5575
process_one_work+0xa0d/0x1c30 kernel/workqueue.c:3314
process_scheduled_works kernel/workqueue.c:3397 [inline]
worker_thread+0x645/0xe80 kernel/workqueue.c:3478
kthread+0x367/0x480 kernel/kthread.c:436
ret_from_fork+0x72b/0xd50 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
Kernel version: commit 8cd9520d35a6c38db6567e97dd93b1f11f185dc6 (tag v7.1)
Relevant kernel config:
CONFIG_MEMCG=y
CONFIG_LRU_GEN=y
CONFIG_LRU_GEN_ENABLED=y
CONFIG_LRU_GEN_WALKS_MMU=y
CONFIG_NUMA=y
Root Cause:
The bug is a race between two code paths that each hold
`lruvec->lru_lock`, but at
non-overlapping times.
Component 1 - `reset_batch_size()`:
During `walk_mm()`, `update_batch_size()` accumulates per-generation
page deltas into
`walk->nr_pages` WITHOUT holding `lruvec_lock`. After
`mmap_read_unlock(mm)`, the
walker reacquires `lruvec_lock` and `reset_batch_size()` writes those deltas
UNCONDITIONALLY into `lrugen->nr_pages`.
Component 2 - `lru_gen_reparent_memcg()`:
When a memcg is offlined, `lru_gen_reparent_memcg()` moves all folios to
the parent
lruvec and zeros the child's `lrugen->nr_pages`, all under `lruvec_lock`.
I have not bisected the issue. Based on code inspection, the important
interaction
appears to be the reparenting path that clears the child's `nr_pages` while
`reset_batch_size()` can still commit a batch that was generated before
the memcg
went offline. This looks related to f304652609ea ("mm: vmscan: prepare for
reparenting MGLRU folios").
Race sequence:
1. The aging path enters walk_mm() for the child memcg lruvec.
2. walk_page_range() scans PTEs and update_batch_size() stores
deltas in
walk->nr_pages. At this point the deltas have not been committed to
lruvec->lrugen.nr_pages yet.
3. walk_mm() drops mmap_read_lock(mm). Before it reaches
reset_batch_size(), the child memcg is killed and removed.
4. The memcg offline path runs lru_gen_reparent_memcg(). Under
lruvec_lock, it moves the child folios to the parent and clears the
child's lrugen.nr_pages.
5. The old aging walk resumes, takes lruvec_lock, and
reset_batch_size()
writes the stale walk->nr_pages deltas back into the original child
lruvec.
6. Later, lru_gen_exit_memcg(child) checks the child's
lrugen.nr_pages with
memchr_inv(...). Since the stale batch made some slots non-zero
again,
VM_WARN_ON_ONCE() triggers.
The two critical sections are serialized by `lruvec_lock`, but the batch
accumulation
in `walk->nr_pages` happens outside that lock, so there is no ordering
between the
accumulation and the reparenting zeroing.
The relevant code path:
mm/vmscan.c:
run_cmd('+') selects the target memcg and child lruvec
try_to_inc_max_seq() stores the child lruvec in walk->lruvec
update_batch_size() accumulates deltas in walk->nr_pages
walk_mm() calls walk_page_range(), then later
reset_batch_size()
reset_batch_size() writes cached deltas into
walk->lruvec->lrugen.nr_pages
lru_gen_reparent_memcg() reparents child MGLRU state and clears
child nr_pages
lru_gen_exit_memcg() warns if the exiting memcg has non-zero
nr_pages
mm/memcontrol.c:
mem_cgroup_css_offline() calls memcg_reparent_objcgs() and
lru_gen_offline_memcg()
mem_cgroup_free() calls lru_gen_exit_memcg()
Reproducer:
The C reproducer and the helper script for running it are provided in
the attachments.
The PoC creates a leaf memory cgroup, moves a victim process into it,
and makes the victim fault and continuously touch file-backed pages so
MGLRU aging can produce cached generation deltas for that memcg. A
separate `lru_ager` thread repeatedly writes aging commands to
`/sys/kernel/debug/lru_gen`; when the instrumentation reports that the
ager is delayed just before `reset_batch_size()`, the PoC kills the
victim and removes the leaf cgroup, forcing memcg offline/reparenting
before the stale batch is committed.
The helper script builds the PoC, creates a temporary qcow2 overlay,
boots the instrumented kernel in QEMU with fake NUMA and SSH port
forwarding, copies the PoC into the guest, runs it, and scans the serial
console for `exit_nonzero`, `WARNING: mm/vmscan.c`, or `Kernel panic`.
It writes the full serial console, extracted kernel events, and guest
stdout/stderr under the chosen output directory.
The example command:
./repros/lru_gen_exit_memcg/run_poc_qemu.sh /tmp/lru_gen_poc_manual
10450 20 32
The arguments are:
/tmp/lru_gen_poc_manual output directory for the overlay, console log,
extracted events and guest log
10450 host TCP port forwarded to guest SSH
20 number of PoC iterations to run
32 file-backed working-set size in MiB per
iteration
The script uses default `KERNEL`, `IMAGE` and `SSH_KEY` paths, or they
can be
overridden with environment variables.
Since this bug requires a specific race window, kernel instrumentation
is needed
to enlarge the race window in order to reproduce the bug more reliably. The
instrumentation patch is also included in the attachments.
The patch only instruments `mm/vmscan.c`: it delays the PoC aging task just
before `reset_batch_size()`, logs when a stale batch is written into an
already
offlined and zeroed memcg lruvec, and dumps the non-zero
`lrugen.nr_pages` slots
before `lru_gen_exit_memcg()` triggers the warning.
A successful run reports `status=repro_triggered`, and the extracted events
include a warning like:
WARNING: mm/vmscan.c:5943 at lru_gen_exit_memcg+0x420/0x520
Proposed Fix:
One possible fix direction is to make `reset_batch_size()` skip writing
back the
stale delta when the memcg is no longer online. `reset_batch_size()` is
called
under `lruvec_lock`, the same lock that `lru_gen_reparent_memcg()` holds
when it
zeroes `nr_pages`, so this should avoid committing a batch after
reparenting has
completed.
Possible fix direction, not a tested patch:
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -... reset_batch_size() ...
static void reset_batch_size(struct lru_gen_mm_walk *walk)
{
int gen, type, zone;
struct lruvec *lruvec = walk->lruvec;
struct lru_gen_folio *lrugen = &lruvec->lrugen;
+ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
walk->batched = 0;
for_each_gen_type_zone(gen, type, zone) {
enum lru_list lru = type * LRU_INACTIVE_FILE;
int delta = walk->nr_pages[gen][type][zone];
if (!delta)
continue;
walk->nr_pages[gen][type][zone] = 0;
+
+ /*
+ * If the memcg went offline while we were walking page tables,
+ * lru_gen_reparent_memcg() has already zeroed nr_pages and moved
+ * all folios to the parent. Writing our stale batch delta back
+ * would corrupt the offline child and trigger WARN_ON in
+ * lru_gen_exit_memcg(). Discard the delta; the parent lruvec
+ * already owns the pages and accounts for them correctly.
+ */
+ if (memcg && !mem_cgroup_online(memcg))
+ continue;
+
WRITE_ONCE(lrugen->nr_pages[gen][type][zone],
lrugen->nr_pages[gen][type][zone] + delta);
if (lru_gen_is_active(lruvec, gen))
lru += LRU_ACTIVE;
__update_lru_size(lruvec, lru, zone, delta);
}
}
Thanks
[-- Attachment #2: poc_lru_race.c --]
[-- Type: text/plain, Size: 10419 bytes --]
/*
* Minimal MGLRU memcg reparent race PoC.
*
* This program expects the companion instrumentation patch to add a short
* delay before reset_batch_size() for cgroups named /lru_gen_race_* and to log
* "delay_before_reset". The program waits for that log line, tears down the
* target memcg, and lets the stale MGLRU batch commit into the offlined child.
*/
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <stdatomic.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#define CGROUP_ROOT "/sys/fs/cgroup"
#define LRU_GEN_FILE "/sys/kernel/debug/lru_gen"
#define PAGE_BYTES 4096UL
#define MAX_NODES 8
#define DEFAULT_ITERS 20
#define DEFAULT_FILE_MIB 32
#define WINDOW_TIMEOUT_MS 90000
#define CONFIRM_TIMEOUT_MS 5000
enum {
PHASE_IDLE = 0,
PHASE_WINDOW = 1,
PHASE_DONE = 2,
};
/* LruInfo stores the debugfs MGLRU ids needed to issue aging commands. */
struct lru_info {
unsigned long memcg_id; /* Memcg id accepted by /sys/kernel/debug/lru_gen. */
int nr_nodes; /* Number of NUMA nodes parsed for this memcg. */
int nodes[MAX_NODES]; /* Node ids parsed from /sys/kernel/debug/lru_gen. */
unsigned long max_seq[MAX_NODES]; /* Latest generation sequence for each node. */
};
/* RaceState is shared by the ager, kmsg reader, and main iteration. */
struct race_state {
char leaf[1024]; /* Absolute cgroup path for the current leaf. */
char leaf_rel[1024]; /* Cgroup path as printed by cgroup_path(). */
char file_path[512]; /* File mapped by the victim process. */
pid_t victim; /* Victim pid charged to the leaf memcg. */
atomic_int phase; /* Current synchronization phase. */
int iter; /* Iteration index used only for concise progress output. */
};
/* Die prints a syscall failure and exits the process. */
static void die(const char *what)
{
perror(what);
exit(1);
}
/* WriteFile writes a short string into a sysfs/cgroupfs control file. */
static int write_file(const char *path, const char *value)
{
int fd = open(path, O_WRONLY | O_CLOEXEC);
if (fd < 0)
return -1;
ssize_t ret = write(fd, value, strlen(value));
int saved_errno = errno;
close(fd);
errno = saved_errno;
return ret < 0 ? -1 : 0;
}
/* MkdirIfMissing creates a cgroup directory if it is not already present. */
static int mkdir_if_missing(const char *path)
{
if (!mkdir(path, 0755) || errno == EEXIST)
return 0;
return -1;
}
/* EnableMemoryController enables memory accounting below a cgroup. */
static void enable_memory_controller(const char *cg)
{
char path[640];
snprintf(path, sizeof(path), "%s/cgroup.subtree_control", cg);
(void)write_file(path, "+memory");
}
/* MovePid moves a process into the target cgroup. */
static int move_pid(const char *cg, pid_t pid)
{
char path[640];
char value[32];
snprintf(path, sizeof(path), "%s/cgroup.procs", cg);
snprintf(value, sizeof(value), "%d", (int)pid);
return write_file(path, value);
}
/* RmdirRetry removes a cgroup after css teardown has made it removable. */
static int rmdir_retry(const char *path)
{
for (int i = 0; i < 600; i++) {
if (!rmdir(path))
return 0;
if (errno != EBUSY && errno != EINVAL)
return -1;
usleep(5000);
}
return -1;
}
/* WaitPhase waits until the shared phase reaches the requested value. */
static bool wait_phase(struct race_state *st, int want, int timeout_ms)
{
for (int i = 0; i < timeout_ms; i++) {
if (atomic_load(&st->phase) >= want)
return true;
usleep(1000);
}
return false;
}
/* VictimMain faults file-backed pages after it has been moved into the leaf. */
static void victim_main(int start_fd, int ready_fd, const char *path, size_t bytes)
{
char ch;
if (read(start_fd, &ch, 1) != 1)
_exit(10);
close(start_fd);
int fd = open(path, O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0600);
if (fd < 0)
_exit(11);
if (ftruncate(fd, (off_t)bytes))
_exit(12);
volatile uint8_t *mapping = mmap(NULL, bytes, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (mapping == MAP_FAILED)
_exit(13);
close(fd);
for (size_t off = 0; off < bytes; off += PAGE_BYTES)
mapping[off] = (uint8_t)(off >> 12);
ssize_t ready_ret = write(ready_fd, "R", 1);
(void)ready_ret;
close(ready_fd);
for (uint8_t seed = 1;; seed++) {
for (size_t off = 0; off < bytes; off += PAGE_BYTES)
mapping[off] ^= seed;
}
}
/* ReadLruInfo parses the target memcg section from /sys/kernel/debug/lru_gen. */
static int read_lru_info(const char *leaf_rel, struct lru_info *info)
{
FILE *file = fopen(LRU_GEN_FILE, "r");
char *line = NULL;
size_t cap = 0;
bool in_target = false;
int current = -1;
int ret = -1;
if (!file)
return -1;
memset(info, 0, sizeof(*info));
while (getline(&line, &cap, file) > 0) {
unsigned long id;
unsigned long seq;
char path[1024];
int node;
if (sscanf(line, " memcg %lu %1023s", &id, path) == 2) {
in_target = !strcmp(path, leaf_rel);
current = -1;
if (in_target) {
info->memcg_id = id;
ret = 0;
}
continue;
}
if (!in_target)
continue;
if (sscanf(line, " node %d", &node) == 1) {
if (info->nr_nodes >= MAX_NODES)
continue;
current = info->nr_nodes++;
info->nodes[current] = node;
continue;
}
if (current >= 0 && sscanf(line, " %lu", &seq) == 1 &&
seq > info->max_seq[current])
info->max_seq[current] = seq;
}
free(line);
fclose(file);
return ret;
}
/* AgerThread repeatedly asks MGLRU debugfs to age the target memcg. */
static void *ager_thread(void *arg)
{
struct race_state *st = arg;
int fd;
prctl(PR_SET_NAME, "lru_ager", 0, 0, 0);
fd = open(LRU_GEN_FILE, O_WRONLY | O_CLOEXEC);
if (fd < 0)
return NULL;
while (atomic_load(&st->phase) < PHASE_WINDOW) {
struct lru_info info;
if (read_lru_info(st->leaf_rel, &info) || !info.memcg_id)
break;
for (int i = 0; i < info.nr_nodes; i++) {
char cmd[128];
if (atomic_load(&st->phase) >= PHASE_WINDOW)
break;
snprintf(cmd, sizeof(cmd), "+ %lu %d %lu 1 1\n",
info.memcg_id, info.nodes[i], info.max_seq[i]);
ssize_t write_ret = write(fd, cmd, strlen(cmd));
(void)write_ret;
}
}
close(fd);
return NULL;
}
/* KmsgThread watches for the instrumentation lines used for synchronization. */
static void *kmsg_thread(void *arg)
{
struct race_state *st = arg;
char buf[8192];
int fd = open("/dev/kmsg", O_RDONLY | O_NONBLOCK | O_CLOEXEC);
if (fd < 0)
return NULL;
lseek(fd, 0, SEEK_END);
while (atomic_load(&st->phase) < PHASE_DONE) {
ssize_t len = read(fd, buf, sizeof(buf) - 1);
const char *msg;
bool ours;
if (len <= 0) {
usleep(500);
continue;
}
buf[len] = '\0';
msg = strchr(buf, ';');
if (msg)
msg++;
else
msg = buf;
ours = strstr(msg, st->leaf_rel) != NULL;
if (ours && strstr(msg, "delay_before_reset")) {
int idle = PHASE_IDLE;
atomic_compare_exchange_strong(&st->phase, &idle, PHASE_WINDOW);
}
if ((ours && strstr(msg, "exit_nonzero")) ||
(atomic_load(&st->phase) >= PHASE_WINDOW &&
strstr(msg, "WARNING: mm/vmscan.c")))
atomic_store(&st->phase, PHASE_DONE);
}
close(fd);
return NULL;
}
/* RunIteration creates one child memcg and races its teardown against aging. */
static bool run_iteration(const char *base, int iter, size_t file_mib)
{
struct race_state st;
int start_pipe[2];
int ready_pipe[2];
pthread_t ager;
pthread_t kmsg;
bool got_window;
bool confirmed = false;
memset(&st, 0, sizeof(st));
st.iter = iter;
snprintf(st.leaf, sizeof(st.leaf), "%s/leaf_%03d", base, iter);
snprintf(st.leaf_rel, sizeof(st.leaf_rel), "%s/leaf_%03d",
base + strlen(CGROUP_ROOT), iter);
snprintf(st.file_path, sizeof(st.file_path), "/root/lru_race_%d_%03d.dat",
getpid(), iter);
atomic_store(&st.phase, PHASE_IDLE);
if (mkdir_if_missing(st.leaf))
return false;
if (pipe(start_pipe) || pipe(ready_pipe))
die("pipe");
st.victim = fork();
if (!st.victim) {
close(start_pipe[1]);
close(ready_pipe[0]);
victim_main(start_pipe[0], ready_pipe[1], st.file_path,
file_mib << 20);
_exit(0);
}
close(start_pipe[0]);
close(ready_pipe[1]);
if (move_pid(st.leaf, st.victim)) {
ssize_t start_ret = write(start_pipe[1], "g", 1);
(void)start_ret;
close(start_pipe[1]);
kill(st.victim, SIGKILL);
waitpid(st.victim, NULL, 0);
rmdir_retry(st.leaf);
return false;
}
ssize_t start_ret = write(start_pipe[1], "g", 1);
(void)start_ret;
close(start_pipe[1]);
char ready;
ssize_t ready_ret = read(ready_pipe[0], &ready, 1);
(void)ready_ret;
close(ready_pipe[0]);
for (int retry = 0; retry < 400; retry++) {
struct lru_info info;
if (!read_lru_info(st.leaf_rel, &info) && info.memcg_id &&
info.nr_nodes > 0)
break;
usleep(5000);
}
pthread_create(&kmsg, NULL, kmsg_thread, &st);
pthread_create(&ager, NULL, ager_thread, &st);
got_window = wait_phase(&st, PHASE_WINDOW, WINDOW_TIMEOUT_MS);
if (got_window) {
kill(st.victim, SIGKILL);
waitpid(st.victim, NULL, 0);
st.victim = 0;
rmdir_retry(st.leaf);
confirmed = wait_phase(&st, PHASE_DONE, CONFIRM_TIMEOUT_MS);
}
atomic_store(&st.phase, PHASE_DONE);
pthread_join(ager, NULL);
pthread_join(kmsg, NULL);
if (st.victim) {
kill(st.victim, SIGKILL);
waitpid(st.victim, NULL, 0);
}
rmdir_retry(st.leaf);
unlink(st.file_path);
printf("iter %d: %s\n", iter, confirmed ? "confirmed" :
got_window ? "window-only" : "miss");
return confirmed;
}
/* Main prepares cgroup/debugfs state and runs bounded race attempts. */
int main(int argc, char **argv)
{
int iters = argc > 1 ? atoi(argv[1]) : DEFAULT_ITERS;
size_t file_mib = argc > 2 ? strtoul(argv[2], NULL, 0) : DEFAULT_FILE_MIB;
char base[512];
int confirmed = 0;
if (geteuid()) {
fprintf(stderr, "must run as root\n");
return 1;
}
if (mount("debugfs", "/sys/kernel/debug", "debugfs", 0, NULL) && errno != EBUSY)
perror("mount debugfs");
enable_memory_controller(CGROUP_ROOT);
snprintf(base, sizeof(base), CGROUP_ROOT "/lru_gen_race_%d", getpid());
if (mkdir_if_missing(base))
die("mkdir base cgroup");
enable_memory_controller(base);
for (int i = 0; i < iters; i++) {
if (run_iteration(base, i, file_mib))
confirmed++;
}
rmdir_retry(base);
printf("confirmed=%d/%d\n", confirmed, iters);
return confirmed ? 0 : 1;
}
[-- Attachment #3: lru_gen_exit_memcg.patch --]
[-- Type: text/plain, Size: 4169 bytes --]
diff --git a/mm/vmscan.c b/mm/vmscan.c
index bd1b1aa12581..6206ce41de3b 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -3265,24 +3265,96 @@ static void update_batch_size(struct lru_gen_mm_walk *walk, struct folio *folio,
walk->nr_pages[new_gen][type][zone] += delta;
}
+/* Return whether any MGLRU size slot is still charged. */
+static bool lru_gen_has_nr_pages(struct lruvec *lruvec)
+{
+ int gen, type, zone;
+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
+
+ for_each_gen_type_zone(gen, type, zone) {
+ if (READ_ONCE(lrugen->nr_pages[gen][type][zone]))
+ return true;
+ }
+
+ return false;
+}
+
+/* Dump nonzero MGLRU size slots for the target memcg. */
+static void lru_gen_dump_nr_pages(const char *tag, struct mem_cgroup *memcg,
+ int nid, struct lruvec *lruvec, bool show_path)
+{
+ int gen, type, zone;
+ char path[256] = "";
+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
+
+ if (show_path && memcg)
+ cgroup_path(memcg->css.cgroup, path, sizeof(path));
+
+ pr_warn("lru_gen_debug: %s task=%s/%d memcg=%llu online=%d dying=%d nid=%d path=%s\n",
+ tag, current->comm, task_pid_nr(current), mem_cgroup_id(memcg),
+ memcg ? mem_cgroup_online(memcg) : 1, memcg_is_dying(memcg),
+ nid, path);
+
+ for_each_gen_type_zone(gen, type, zone) {
+ long nr_pages = READ_ONCE(lrugen->nr_pages[gen][type][zone]);
+
+ if (!nr_pages)
+ continue;
+
+ pr_warn("lru_gen_debug: %s slot memcg=%llu nid=%d gen=%d type=%d zone=%d nr=%ld list_empty=%d\n",
+ tag, mem_cgroup_id(memcg), nid, gen, type, zone,
+ nr_pages, list_empty(&lrugen->folios[gen][type][zone]));
+ }
+}
+
+/* Delay the PoC aging task so memcg offline can race with batch reset. */
+static void lru_gen_delay_test_reset(struct lruvec *lruvec)
+{
+ char path[128] = "";
+ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
+
+ if (!memcg || strcmp(current->comm, "lru_ager"))
+ return;
+
+ cgroup_path(memcg->css.cgroup, path, sizeof(path));
+ if (!str_has_prefix(path, "/lru_gen_race_"))
+ return;
+
+ pr_warn("lru_gen_debug: delay_before_reset task=%s/%d memcg=%llu online=%d dying=%d path=%s\n",
+ current->comm, task_pid_nr(current), mem_cgroup_id(memcg),
+ mem_cgroup_online(memcg), memcg_is_dying(memcg), path);
+ msleep(3000);
+}
+
static void reset_batch_size(struct lru_gen_mm_walk *walk)
{
int gen, type, zone;
struct lruvec *lruvec = walk->lruvec;
struct lru_gen_folio *lrugen = &lruvec->lrugen;
+ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
+ bool offline = memcg && (!mem_cgroup_online(memcg) || memcg_is_dying(memcg));
+ bool zeroed = offline && !lru_gen_has_nr_pages(lruvec);
walk->batched = 0;
for_each_gen_type_zone(gen, type, zone) {
enum lru_list lru = type * LRU_INACTIVE_FILE;
int delta = walk->nr_pages[gen][type][zone];
+ long old;
if (!delta)
continue;
walk->nr_pages[gen][type][zone] = 0;
- WRITE_ONCE(lrugen->nr_pages[gen][type][zone],
- lrugen->nr_pages[gen][type][zone] + delta);
+ old = READ_ONCE(lrugen->nr_pages[gen][type][zone]);
+ WRITE_ONCE(lrugen->nr_pages[gen][type][zone], old + delta);
+
+ if (zeroed)
+ pr_warn("lru_gen_debug: reset_batch_to_zeroed_offline task=%s/%d memcg=%llu online=%d dying=%d nid=%d seq=%lu gen=%d type=%d zone=%d delta=%d old=%ld new=%ld\n",
+ current->comm, task_pid_nr(current), mem_cgroup_id(memcg),
+ mem_cgroup_online(memcg), memcg_is_dying(memcg),
+ lruvec_pgdat(lruvec)->node_id, walk->seq, gen, type,
+ zone, delta, old, old + delta);
if (lru_gen_is_active(lruvec, gen))
lru += LRU_ACTIVE;
@@ -3783,6 +3855,7 @@ static void walk_mm(struct mm_struct *mm, struct lru_gen_mm_walk *walk)
}
if (walk->batched) {
+ lru_gen_delay_test_reset(lruvec);
lruvec_lock_irq(lruvec);
reset_batch_size(walk);
lruvec_unlock_irq(lruvec);
@@ -5864,6 +5937,9 @@ void lru_gen_exit_memcg(struct mem_cgroup *memcg)
struct lruvec *lruvec = get_lruvec(memcg, nid);
struct lru_gen_mm_state *mm_state = get_mm_state(lruvec);
+ if (lru_gen_has_nr_pages(lruvec))
+ lru_gen_dump_nr_pages("exit_nonzero", memcg, nid, lruvec, true);
+
VM_WARN_ON_ONCE(memchr_inv(lruvec->lrugen.nr_pages, 0,
sizeof(lruvec->lrugen.nr_pages)));
[-- Attachment #4: run_poc_qemu.sh --]
[-- Type: application/x-sh, Size: 3160 bytes --]
^ permalink raw reply related
* [PATCH] blk-iolatency: flush enable work after policy deactivation
From: Cen Zhang @ 2026-06-21 13:59 UTC (permalink / raw)
To: Tejun Heo, Josef Bacik, Jens Axboe
Cc: cgroups, linux-block, linux-kernel, baijiaju1990, zzzccc427
A blk-iolatency rq-qos teardown can free struct blk_iolatency while a
freshly queued enable_work callback still references it. The observed
failure is:
blkcg_iolatency_exit() flushes enable_work before deactivating the
iolatency policy. However, blkcg_deactivate_policy() calls
iolatency_pd_offline() for online policy data, and iolatency_pd_offline()
clears min_lat_nsec through iolatency_set_min_lat_nsec(). If this clears
the last nonzero latency target, enable_cnt reaches zero and schedules
enable_work again after the flush has already returned.
The buggy scenario involves two paths, with each column showing the order
within that path:
blkcg_iolatency_exit() path: system_wq worker path:
1. Flush old enable_work. 1. enable_work is idle.
2. Deactivate the policy. 2. no worker owns it.
3. Offline queues new enable_work. 3. work item becomes pending.
4. Free blkiolat. 4. worker later runs the item.
5. Owner storage is gone. 5. worker dereferences blkiolat.
Flush enable_work again after blkcg_deactivate_policy() returns and before
freeing blkiolat. Policy offline callbacks have completed at that point,
so the second drain covers the late queueing path without changing the
normal enable/disable accounting rules.
Validation reproduced this kernel report:
BUG: KASAN: slab-use-after-free in assign_work+0x2a/0x150
Call Trace:
<TASK>
dump_stack_lvl+0x53/0x70
print_report+0xd0/0x630
? __pfx__raw_spin_lock_irqsave+0x10/0x10
? srso_alias_return_thunk+0x5/0xfbef5
? __virt_addr_valid+0xea/0x1a0
? assign_work+0x2a/0x150
kasan_report+0xce/0x100
? assign_work+0x2a/0x150
assign_work+0x2a/0x150
worker_thread+0x1b7/0x500
? __pfx_worker_thread+0x10/0x10
kthread+0x192/0x1d0
? __pfx_kthread+0x10/0x10
ret_from_fork+0x2ac/0x3c0
? __pfx_ret_from_fork+0x10/0x10
? srso_alias_return_thunk+0x5/0xfbef5
? __switch_to+0x2d5/0x6e0
? __pfx_kthread+0x10/0x10
ret_from_fork_asm+0x1a/0x30
</TASK>
Allocated by task 470:
kasan_save_stack+0x33/0x60
kasan_save_track+0x14/0x30
__kasan_kmalloc+0x8f/0xa0
iolatency_set_limit+0x301/0x450
cgroup_file_write+0x178/0x2e0
kernfs_fop_write_iter+0x1ef/0x290
vfs_write+0x446/0x6f0
ksys_write+0xc7/0x160
do_syscall_64+0xf9/0x540
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Freed by task 611:
kasan_save_stack+0x33/0x60
kasan_save_track+0x14/0x30
kasan_save_free_info+0x3b/0x60
__kasan_slab_free+0x43/0x70
kfree+0x131/0x390
rq_qos_exit+0x5d/0x90
__del_gendisk+0x394/0x490
del_gendisk+0xa1/0xe0
virtblk_remove+0x41/0xd0
virtio_dev_remove+0x63/0xe0
device_release_driver_internal+0x246/0x2e0
unbind_store+0xa9/0xb0
kernfs_fop_write_iter+0x1ef/0x290
vfs_write+0x446/0x6f0
ksys_write+0xc7/0x160
do_syscall_64+0xf9/0x540
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Last potentially related work creation:
kasan_save_stack+0x33/0x60
kasan_record_aux_stack+0x8c/0xa0
__queue_work+0x42a/0x800
queue_work_on+0x5d/0x70
iolatency_set_min_lat_nsec+0x196/0x230
iolatency_pd_offline+0x1f/0x40
blkcg_deactivate_policy+0x194/0x270
blkcg_iolatency_exit+0x33/0x40
rq_qos_exit+0x5d/0x90
__del_gendisk+0x394/0x490
del_gendisk+0xa1/0xe0
virtblk_remove+0x41/0xd0
virtio_dev_remove+0x63/0xe0
device_release_driver_internal+0x246/0x2e0
unbind_store+0xa9/0xb0
kernfs_fop_write_iter+0x1ef/0x290
vfs_write+0x446/0x6f0
ksys_write+0xc7/0x160
do_syscall_64+0xf9/0x540
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Second to last potentially related work creation:
kasan_save_stack+0x33/0x60
kasan_record_aux_stack+0x8c/0xa0
__queue_work+0x42a/0x800
queue_work_on+0x5d/0x70
iolatency_set_min_lat_nsec+0x196/0x230
iolatency_set_limit+0x3f1/0x450
cgroup_file_write+0x178/0x2e0
kernfs_fop_write_iter+0x1ef/0x290
vfs_write+0x446/0x6f0
ksys_write+0xc7/0x160
do_syscall_64+0xf9/0x540
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Fixes: 8a177a36da6c ("blk-iolatency: Fix inflight count imbalances and IO hangs on offline")
Assisted-by: Codex:gpt-5.5
Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
---
block/blk-iolatency.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/block/blk-iolatency.c b/block/blk-iolatency.c
index 1aaee6fb0f59..a0bdd8a5c94c 100644
--- a/block/blk-iolatency.c
+++ b/block/blk-iolatency.c
@@ -639,6 +639,11 @@ static void blkcg_iolatency_exit(struct rq_qos *rqos)
timer_shutdown_sync(&blkiolat->timer);
flush_work(&blkiolat->enable_work);
blkcg_deactivate_policy(rqos->disk, &blkcg_policy_iolatency);
+ /*
+ * blkcg_deactivate_policy() invokes iolatency_pd_offline(), which may
+ * queue enable_work again when it clears the last latency target.
+ */
+ flush_work(&blkiolat->enable_work);
kfree(blkiolat);
}
--
2.43.0
^ permalink raw reply related
* [PATCH] block, bfq: protect async queue reset with blkcg locks
From: Cen Zhang @ 2026-06-21 13:59 UTC (permalink / raw)
To: Yu Kuai, Tejun Heo, Josef Bacik, Jens Axboe, Arianna Avanzini,
Paolo Valente
Cc: linux-block, cgroups, linux-kernel, baijiaju1990, zzzccc427
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:
<TASK>
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 <zzzccc427@gmail.com>
---
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)
--
2.43.0
^ permalink raw reply related
* Re: [PATCH] block, bfq: protect async queue reset with blkcg locks
From: Tao Cui @ 2026-06-21 18:33 UTC (permalink / raw)
To: Cen Zhang, Yu Kuai, Tejun Heo, Josef Bacik, Jens Axboe,
Arianna Avanzini, Paolo Valente
Cc: cui.tao, linux-block, cgroups, linux-kernel, baijiaju1990
In-Reply-To: <20260621135930.2657810-1-zzzccc427@gmail.com>
Nice catch. The race is real, and the fix lines up with how the rest
of the blkcg code already protects blkg_list walks — the new nesting
(blkcg_mutex -> queue_lock -> bfqd->lock) is the same order
blkg_free_workfn() and bfq_pd_offline() use, so no inversion.
Reviewed-by: Tao Cui <cuitao@kylinos.cn>
在 2026/6/21 21:59, Cen Zhang 写道:
> 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.
^ permalink raw reply
* Re: [PATCH v7 1/9] cgroup/cpuset: rebind mm mempolicy to effective_mems, not mems_allowed
From: Ridong Chen @ 2026-06-22 1:42 UTC (permalink / raw)
To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutný,
Li Zefan, Farhad Alemi, Andrew Morton
Cc: cgroups, linux-kernel, Aaron Tomlin, Guopeng Zhang, Gregory Price,
David Hildenbrand, stable
In-Reply-To: <20260621032816.1806773-2-longman@redhat.com>
On 6/21/2026 11:28 AM, Waiman Long wrote:
> From: Farhad Alemi <farhad.alemi@berkeley.edu>
>
> Creating a child cpuset where cpuset.mems is never set leads to a div/0
> when a VMA mempolicy with MPOL_F_RELATIVE_NODES rebinds in response to a
> CPU hotplug event.
>
> Reproduction steps:
> 1) Create a cgroup w/ cpuset controls (do not set cpuset.mems)
> 2) Move the task into the child cpuset
> 3) Create a VMA mempolicy for that task with MPOL_F_RELATIVE_NODES
> 4) unplug and hotplug a cpu
> echo 0 > /sys/devices/system/cpu/cpu1/online
> echo 1 > /sys/devices/system/cpu/cpu1/online
> 5) mempolicy rebind does a div/0 in mpol_relative_nodemask on the
> call to __nodes_fold()
>
> The cpuset code passes (cs->mems_allowed) which is not guaranteed to have
> nodes to the rebind routine. Use cs->effective_mems instead, which is
> guaranteed to have a non-empty nodemask.
>
> Closes: https://lore.kernel.org/linux-mm/CA+0ovCgxbZkXa+OU8w3s84R3KNPNxxRfmsNR-udh+afQBbGNmw@mail.gmail.com/
> Link: https://lore.kernel.org/all/CA+0ovCiEz6SP_sn3kN4Tb+_oC=eHMXy_Ffj=usV3wREdQrUtww@mail.gmail.com/
> Fixes: ae1c802382f7 ("cpuset: apply cs->effective_{cpus,mems}")
> Suggested-by: Gregory Price <gourry@gourry.net>
> Suggested-by: Waiman Long <longman@redhat.com>
> Signed-off-by: Farhad Alemi <farhad.alemi@berkeley.edu>
> Acked-by: Waiman Long <longman@redhat.com>
> Cc: stable@vger.kernel.org
> ---
> kernel/cgroup/cpuset.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
> index 591e3aa487fc..b21c31650583 100644
> --- a/kernel/cgroup/cpuset.c
> +++ b/kernel/cgroup/cpuset.c
> @@ -2653,7 +2653,7 @@ void cpuset_update_tasks_nodemask(struct cpuset *cs)
>
> migrate = is_memory_migrate(cs);
>
> - mpol_rebind_mm(mm, &cs->mems_allowed);
> + mpol_rebind_mm(mm, &cs->effective_mems);
> if (migrate)
> cpuset_migrate_mm(mm, &cs->old_mems_allowed, &newmems);
> else
Reviewed-by: Ridong Chen <ridong.chen@linux.dev>
--
Best regards
Ridong
^ permalink raw reply
* Re: [PATCH v7 3/9] cgroup/cpuset: Prevent race between task attach and cpuset state change
From: Ridong Chen @ 2026-06-22 2:21 UTC (permalink / raw)
To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutný,
Li Zefan, Farhad Alemi, Andrew Morton
Cc: cgroups, linux-kernel, Aaron Tomlin, Guopeng Zhang, Gregory Price,
David Hildenbrand
In-Reply-To: <20260621032816.1806773-4-longman@redhat.com>
On 6/21/2026 11:28 AM, Waiman Long wrote:
> Commit e44193d39e8d ("cpuset: let hotplug propagation work wait for
> task attaching") was introduced to let hotplug operation to wait
> until the completion of task attaching operation. However, it is
> still possible that the states of the source or destination cpuset
> can be changed between the cpuset_can_attach() call and the subsequent
> cpuset_attach()/cpuset_cacnel_attach() call.
>
> As a result, data gathered during cpuset_can_attach() cannot be reliably
> used in the subsequent cpuset_attach()/cpuset_cacnel_attach()
> call at all. Make the task attach operation more robust
> and allow the sharing of data between cpuset_can_attach() and
> cpuset_attach()/cpuset_cacnel_attach() by making cpuset_write_resmask()
> and cpuset_partition_write() wait for the completion of task attach
> and set the attach_in_progress flag in the source cpuset as well.
>
> The comments about validate_change() are no longer valid as it won't
> be called at all if an attach operation is in progress. So the comments
> can be removed.
>
> Signed-off-by: Waiman Long <longman@redhat.com>
> ---
> kernel/cgroup/cpuset.c | 28 ++++++++++++++++++++--------
> 1 file changed, 20 insertions(+), 8 deletions(-)
>
> diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
> index a1c8890d3519..65d095dcada1 100644
> --- a/kernel/cgroup/cpuset.c
> +++ b/kernel/cgroup/cpuset.c
> @@ -3080,11 +3080,8 @@ static int cpuset_can_attach(struct cgroup_taskset *tset)
> cs->dl_bw_cpu = cpu;
>
> out_success:
> - /*
> - * Mark attach is in progress. This makes validate_change() fail
> - * changes which zero cpus/mems_allowed.
> - */
> cs->attach_in_progress++;
> + oldcs->attach_in_progress++;
>
I only see oldcs->attach_in_progress being incremented here — the
matching decrement seems to land in a later patch. That makes this one
unbalanced on its own (the count would leak, and a later write to the
source cpuset would block on the new wait_event()), so it's not bisect-safe.
Let's either keep the patch self-contained or fold it into the patch
that adds the decrement.
> out_unlock:
> if (ret)
> @@ -3235,10 +3232,19 @@ ssize_t cpuset_write_resmask(struct kernfs_open_file *of,
> return -EACCES;
>
> buf = strstrip(buf);
> +retry:
> + wait_event(cpuset_attach_wq, cs->attach_in_progress == 0);
> +
> cpuset_full_lock();
> if (!is_cpuset_online(cs))
> goto out_unlock;
>
> + /* Don't race with task attach */
> + if (cs->attach_in_progress) {
> + cpuset_full_unlock();
> + goto retry;
> + }
> +
> trialcs = dup_or_alloc_cpuset(cs);
> if (!trialcs) {
> retval = -ENOMEM;
> @@ -3366,7 +3372,17 @@ static ssize_t cpuset_partition_write(struct kernfs_open_file *of, char *buf,
> else
> return -EINVAL;
>
> +retry:
> + wait_event(cpuset_attach_wq, cs->attach_in_progress == 0);
> +
> cpuset_full_lock();
> +
> + /* Don't race with task attach */
> + if (cs->attach_in_progress) {
> + cpuset_full_unlock();
> + goto retry;
> + }
> +
> if (is_cpuset_online(cs))
> retval = update_prstate(cs, val);
> cpuset_update_sd_hk_unlock();
> @@ -3605,10 +3621,6 @@ static int cpuset_can_fork(struct task_struct *task, struct css_set *cset)
> if (ret)
> goto out_unlock;
>
> - /*
> - * Mark attach is in progress. This makes validate_change() fail
> - * changes which zero cpus/mems_allowed.
> - */
> cs->attach_in_progress++;
> out_unlock:
> mutex_unlock(&cpuset_mutex);
--
Best regards
Ridong
^ permalink raw reply
* Re: [PATCH v7 7/9] cgroup/cpuset: Move mpol_rebind_mm/cpuset_migrate_mm() calls inside cpuset_attach_task()
From: Ridong Chen @ 2026-06-22 2:48 UTC (permalink / raw)
To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutný,
Li Zefan, Farhad Alemi, Andrew Morton
Cc: cgroups, linux-kernel, Aaron Tomlin, Guopeng Zhang, Gregory Price,
David Hildenbrand
In-Reply-To: <20260621032816.1806773-8-longman@redhat.com>
On 6/21/2026 11:28 AM, Waiman Long wrote:
> The cpuset_attach_task() was introduced in commit 42a11bf5c543
> ("cgroup/cpuset: Make cpuset_fork() handle CLONE_INTO_CGROUP properly")
> to enable the CLONE_INTO_CGROUP flag of clone(2) to behave more like
> moving a task from one cpuset into another one. That commits didn't
> move the mpol_rebind_mm() and cpuset_migrate_mm() calls for group leader
> into cpuset_attach_task().
>
> When the CLONE_INTO_CGROUP flag is used without CLONE_THREAD, the new
> task is its own group leader. So it is still not equivalent to moving
> task between cpusets in this case. Make CLONE_INTO_CGROUP behaves
> more close to cpuset_attach() by moving the mpol_rebind_mm() and
> cpuset_migrate_mm() calls inside cpuset_attach_task(). As a result,
> the following static variables will have to be updated in cpuset_fork().
> - cpuset_attach_old_cs
> - attach_cpus_updated
> - attach_mems_updated
> - queue_task_work
>
> Signed-off-by: Waiman Long <longman@redhat.com>
> ---
> kernel/cgroup/cpuset.c | 105 ++++++++++++++++++++++++-----------------
> 1 file changed, 62 insertions(+), 43 deletions(-)
>
> diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
> index 0375dae26d0b..511afb077e2d 100644
> --- a/kernel/cgroup/cpuset.c
> +++ b/kernel/cgroup/cpuset.c
> @@ -2981,8 +2981,13 @@ static int update_prstate(struct cpuset *cs, int new_prs)
> /*
> * cpuset_can_attach() and cpuset_attach() specific internal data
> * Protected by cpuset_mutex
> + *
> + * The attach_cpus_updated/attach_mems_updated flags are set in either
> + * cpuset_attach() or cpuset_fork() and used in cpuset_attach_task().
> */
> static struct cpuset *cpuset_attach_old_cs;
> +static bool attach_cpus_updated;
> +static bool attach_mems_updated;
>
> /*
> * Check to see if a cpuset can accept a new task
> @@ -3157,9 +3162,12 @@ static void cpuset_cancel_attach(struct cgroup_taskset *tset)
> */
> static cpumask_var_t cpus_attach;
> static nodemask_t cpuset_attach_nodemask_to;
> +static bool queue_task_work;
>
There are more and more of these standalone state variables now, and
it's getting harder to maintain. Could we group them into a struct and
manage them together rather than keep adding globals?
Just like:
```
struct cpuset_attach_ctx {
struct cpuset *old_cs;
struct llist_head src_cs, dst_cs;
bool cpus_updated, mems_updated, queue_work;
nodemask_t nodemask_to;
};
```
> static void cpuset_attach_task(struct cpuset *cs, struct task_struct *task)
> {
> + struct mm_struct *mm;
> +
> lockdep_assert_cpuset_lock_held();
>
> if (cs != &top_cpuset)
> @@ -3173,28 +3181,60 @@ static void cpuset_attach_task(struct cpuset *cs, struct task_struct *task)
> */
> WARN_ON_ONCE(set_cpus_allowed_ptr(task, cpus_attach));
>
> + if (cpuset_v2() && !attach_mems_updated)
> + return;
> +
> cpuset_change_task_nodemask(task, &cpuset_attach_nodemask_to);
> cpuset1_update_task_spread_flags(cs, task);
> +
> + if ((task != task->group_leader) ||
> + (!is_memory_migrate(cs) && !attach_mems_updated))
> + return;
> +
> + /*
> + * Change mm for threadgroup leader. This is expensive and may
> + * sleep and should be moved outside migration path proper.
> + */
> + mm = get_task_mm(task);
> + if (mm) {
> + struct cpuset *oldcs = cpuset_attach_old_cs;
> +
> + mpol_rebind_mm(mm, &cs->effective_mems);
> +
> + /*
> + * old_mems_allowed is the same with mems_allowed
> + * here, except if this task is being moved
> + * automatically due to hotplug. In that case
> + * @mems_allowed has been updated and is empty, so
> + * @old_mems_allowed is the right nodesets that we
> + * migrate mm from.
> + */
> + if (is_memory_migrate(cs)) {
> + cpuset_migrate_mm(mm, &oldcs->old_mems_allowed,
> + &cpuset_attach_nodemask_to);
> + queue_task_work = true;
> + } else {
> + mmput(mm);
> + }
> + }
> }
>
> static void cpuset_attach(struct cgroup_taskset *tset)
> {
> struct task_struct *task;
> - struct task_struct *leader;
> struct cgroup_subsys_state *css;
> struct cpuset *cs;
> struct cpuset *oldcs = cpuset_attach_old_cs;
> - bool cpus_updated, mems_updated;
> - bool queue_task_work = false;
>
> cgroup_taskset_first(tset, &css);
> cs = css_cs(css);
>
> lockdep_assert_cpus_held(); /* see cgroup_attach_lock() */
> mutex_lock(&cpuset_mutex);
> - cpus_updated = !cpumask_equal(cs->effective_cpus,
> - oldcs->effective_cpus);
> - mems_updated = !nodes_equal(cs->effective_mems, oldcs->effective_mems);
> + queue_task_work = false;
> +
> + attach_cpus_updated = !cpumask_equal(cs->effective_cpus, oldcs->effective_cpus);
> + attach_mems_updated = !nodes_equal(cs->effective_mems, oldcs->effective_mems);
> guarantee_online_mems(cs, &cpuset_attach_nodemask_to);
>
> /*
> @@ -3203,44 +3243,12 @@ static void cpuset_attach(struct cgroup_taskset *tset)
> * and mems. In that case, we can optimize out by skipping the task
> * iteration and update.
> */
> - if (cpuset_v2() && !cpus_updated && !mems_updated)
> + if (cpuset_v2() && !attach_cpus_updated && !attach_mems_updated)
> goto out;
>
> cgroup_taskset_for_each(task, css, tset)
> cpuset_attach_task(cs, task);
>
> - /*
> - * Change mm for all threadgroup leaders. This is expensive and may
> - * sleep and should be moved outside migration path proper. Skip it
> - * if there is no change in effective_mems and CS_MEMORY_MIGRATE is
> - * not set.
> - */
> - if (!is_memory_migrate(cs) && !mems_updated)
> - goto out;
> -
> - cgroup_taskset_for_each_leader(leader, css, tset) {
> - struct mm_struct *mm = get_task_mm(leader);
> -
> - if (mm) {
> - mpol_rebind_mm(mm, &cs->effective_mems);
> -
> - /*
> - * old_mems_allowed is the same with mems_allowed
> - * here, except if this task is being moved
> - * automatically due to hotplug. In that case
> - * @mems_allowed has been updated and is empty, so
> - * @old_mems_allowed is the right nodesets that we
> - * migrate mm from.
> - */
> - if (is_memory_migrate(cs)) {
> - cpuset_migrate_mm(mm, &oldcs->old_mems_allowed,
> - &cpuset_attach_nodemask_to);
> - queue_task_work = true;
> - } else
> - mmput(mm);
> - }
> - }
> -
> out:
> if (queue_task_work)
> schedule_flush_migrate_mm();
> @@ -3689,15 +3697,14 @@ static void cpuset_cancel_fork(struct task_struct *task, struct css_set *cset)
> */
> static void cpuset_fork(struct task_struct *task)
> {
> - struct cpuset *cs;
> - bool same_cs;
> + struct cpuset *cs, *oldcs;
>
> rcu_read_lock();
> cs = task_cs(task);
> - same_cs = (cs == task_cs(current));
> + oldcs = task_cs(current);
> rcu_read_unlock();
>
> - if (same_cs) {
> + if (cs == oldcs) {
> if (cs == &top_cpuset)
> return;
>
> @@ -3709,7 +3716,19 @@ static void cpuset_fork(struct task_struct *task)
> /* CLONE_INTO_CGROUP */
> mutex_lock(&cpuset_mutex);
> guarantee_online_mems(cs, &cpuset_attach_nodemask_to);
> + cs->old_mems_allowed = cpuset_attach_nodemask_to;
> +
> + /*
> + * Assume CPUs and memory nodes are updated
> + * A CLONE_INTO_CGROUP operation should have taken the cgroup mutex
> + * and so there shouldn't be a competing cpuset_attach() operation.
> + */
> + attach_cpus_updated = attach_mems_updated = true;
> + queue_task_work = false;
> + cpuset_attach_old_cs = oldcs;
> cpuset_attach_task(cs, task);
> + if (queue_task_work)
> + schedule_flush_migrate_mm();
>
> dec_attach_in_progress_locked(cs);
> mutex_unlock(&cpuset_mutex);
--
Best regards
Ridong
^ permalink raw reply
* Re: [BUG] mm: mglru: stale aging batch triggers lru_gen_exit_memcg warning
From: Qi Zheng @ 2026-06-22 3:12 UTC (permalink / raw)
To: Peiyang He, akpm, hannes, linux-mm
Cc: mhocko, roman.gushchin, shakeel.butt, muchun.song, kasong, baohua,
axelrasmussen, yuanchu, weixugc, david, ljs, cgroups,
linux-kernel, syzkaller
In-Reply-To: <5A9E929D82717101+12fcf643-efb8-4b9a-a53a-1e28cc894f0b@smail.nju.edu.cn>
Hi Peiyang,
Thanks for reporting this issue!
On 6/21/26 9:50 PM, Peiyang He wrote:
> Hello,
>
> I hit the following warning while fuzzing other kernel code with Syzkaller.
>
> The original Syzkaller report:
>
> WARNING: mm/vmscan.c:5867 at lru_gen_exit_memcg+0x26f/0x300 mm/
> vmscan.c:5867, CPU#0: kworker/0:0/9
> Modules linked in:
> CPU: 0 UID: 0 PID: 9 Comm: kworker/0:0 Not tainted 7.1.0 #2 PREEMPT(full)
> Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix,
> 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
> Workqueue: cgroup_free css_free_rwork_fn
> RIP: 0010:lru_gen_exit_memcg+0x26f/0x300 mm/vmscan.c:5867
> Code: 89 de e8 d4 62 ba ff 49 83 fd 3f 0f 86 9c fe ff ff 48 83 c4 08 5b
> 5d 41 5c 41 5d 41 5e 41 5f e9 17 68 ba ff e8 12 68 ba ff 90 <0f> 0b 90
> e9 b0 fe ff ff e8 04 68 ba ff 66 90 e8 fd 67 ba ff 90 0f
> RSP: 0018:ffffc900001afb78 EFLAGS: 00010293
> RAX: 0000000000000000 RBX: 0000000000000000 RCX: ffffffff82049e88
> RDX: ffff888016f35c40 RSI: ffffffff8204a02e RDI: ffff88801d4103b8
> RBP: dffffc0000000000 R08: 0000000000000005 R09: 0000000000000040
> R10: 0000000000000000 R11: 0000000000002ba4 R12: ffff8880481f1600
> R13: ffff88801d410650 R14: ffff88801d410040 R15: dead000000000100
> FS: 0000000000000000(0000) GS:ffff888098d91000(0000)
> knlGS:0000000000000000
> CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> CR2: 000055ac6490c1d8 CR3: 00000000249b0000 CR4: 0000000000350ef0
> Call Trace:
> <TASK>
> mem_cgroup_free mm/memcontrol.c:3972 [inline]
> mem_cgroup_css_free+0x76/0xb0 mm/memcontrol.c:4241
> css_free_rwork_fn+0x125/0x1260 kernel/cgroup/cgroup.c:5575
> process_one_work+0xa0d/0x1c30 kernel/workqueue.c:3314
> process_scheduled_works kernel/workqueue.c:3397 [inline]
> worker_thread+0x645/0xe80 kernel/workqueue.c:3478
> kthread+0x367/0x480 kernel/kthread.c:436
> ret_from_fork+0x72b/0xd50 arch/x86/kernel/process.c:158
> ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
> </TASK>
>
> Kernel version: commit 8cd9520d35a6c38db6567e97dd93b1f11f185dc6 (tag v7.1)
>
> Relevant kernel config:
>
> CONFIG_MEMCG=y
> CONFIG_LRU_GEN=y
> CONFIG_LRU_GEN_ENABLED=y
> CONFIG_LRU_GEN_WALKS_MMU=y
> CONFIG_NUMA=y
>
> Root Cause:
>
> The bug is a race between two code paths that each hold `lruvec-
> >lru_lock`, but at
> non-overlapping times.
>
> Component 1 - `reset_batch_size()`:
>
> During `walk_mm()`, `update_batch_size()` accumulates per-generation
> page deltas into
> `walk->nr_pages` WITHOUT holding `lruvec_lock`. After
> `mmap_read_unlock(mm)`, the
> walker reacquires `lruvec_lock` and `reset_batch_size()` writes those
> deltas
> UNCONDITIONALLY into `lrugen->nr_pages`.
>
> Component 2 - `lru_gen_reparent_memcg()`:
>
> When a memcg is offlined, `lru_gen_reparent_memcg()` moves all folios to
> the parent
> lruvec and zeros the child's `lrugen->nr_pages`, all under `lruvec_lock`.
>
> I have not bisected the issue. Based on code inspection, the important
> interaction
> appears to be the reparenting path that clears the child's `nr_pages` while
> `reset_batch_size()` can still commit a batch that was generated before
> the memcg
> went offline. This looks related to f304652609ea ("mm: vmscan: prepare for
> reparenting MGLRU folios").
>
> Race sequence:
>
> 1. The aging path enters walk_mm() for the child memcg lruvec.
>
> 2. walk_page_range() scans PTEs and update_batch_size() stores
> deltas in
> walk->nr_pages. At this point the deltas have not been
> committed to
> lruvec->lrugen.nr_pages yet.
>
> 3. walk_mm() drops mmap_read_lock(mm). Before it reaches
> reset_batch_size(), the child memcg is killed and removed.
>
> 4. The memcg offline path runs lru_gen_reparent_memcg(). Under
> lruvec_lock, it moves the child folios to the parent and clears the
> child's lrugen.nr_pages.
>
> 5. The old aging walk resumes, takes lruvec_lock, and
> reset_batch_size()
> writes the stale walk->nr_pages deltas back into the original child
> lruvec.
>
> 6. Later, lru_gen_exit_memcg(child) checks the child's
> lrugen.nr_pages with
> memchr_inv(...). Since the stale batch made some slots non-zero
> again,
> VM_WARN_ON_ONCE() triggers.
It seems this race can actually happen.
>
> The two critical sections are serialized by `lruvec_lock`, but the batch
> accumulation
> in `walk->nr_pages` happens outside that lock, so there is no ordering
> between the
> accumulation and the reparenting zeroing.
>
> The relevant code path:
>
> mm/vmscan.c:
> run_cmd('+') selects the target memcg and child lruvec
> try_to_inc_max_seq() stores the child lruvec in walk->lruvec
> update_batch_size() accumulates deltas in walk->nr_pages
> walk_mm() calls walk_page_range(), then later
> reset_batch_size()
> reset_batch_size() writes cached deltas into walk->lruvec-
> >lrugen.nr_pages
> lru_gen_reparent_memcg() reparents child MGLRU state and clears
> child nr_pages
> lru_gen_exit_memcg() warns if the exiting memcg has non-zero
> nr_pages
>
> mm/memcontrol.c:
> mem_cgroup_css_offline() calls memcg_reparent_objcgs() and
> lru_gen_offline_memcg()
> mem_cgroup_free() calls lru_gen_exit_memcg()
>
> Reproducer:
>
> The C reproducer and the helper script for running it are provided in
> the attachments.
>
> The PoC creates a leaf memory cgroup, moves a victim process into it,
> and makes the victim fault and continuously touch file-backed pages so
> MGLRU aging can produce cached generation deltas for that memcg. A
> separate `lru_ager` thread repeatedly writes aging commands to `/sys/
> kernel/debug/lru_gen`; when the instrumentation reports that the ager is
> delayed just before `reset_batch_size()`, the PoC kills the victim and
> removes the leaf cgroup, forcing memcg offline/reparenting before the
> stale batch is committed.
>
> The helper script builds the PoC, creates a temporary qcow2 overlay,
> boots the instrumented kernel in QEMU with fake NUMA and SSH port
> forwarding, copies the PoC into the guest, runs it, and scans the serial
> console for `exit_nonzero`, `WARNING: mm/vmscan.c`, or `Kernel panic`.
> It writes the full serial console, extracted kernel events, and guest
> stdout/stderr under the chosen output directory.
>
> The example command:
>
> ./repros/lru_gen_exit_memcg/run_poc_qemu.sh /tmp/lru_gen_poc_manual
> 10450 20 32
>
> The arguments are:
>
> /tmp/lru_gen_poc_manual output directory for the overlay, console log,
> extracted events and guest log
> 10450 host TCP port forwarded to guest SSH
> 20 number of PoC iterations to run
> 32 file-backed working-set size in MiB per
> iteration
>
> The script uses default `KERNEL`, `IMAGE` and `SSH_KEY` paths, or they
> can be
> overridden with environment variables.
>
> Since this bug requires a specific race window, kernel instrumentation
> is needed
> to enlarge the race window in order to reproduce the bug more reliably.
> The
> instrumentation patch is also included in the attachments.
>
> The patch only instruments `mm/vmscan.c`: it delays the PoC aging task just
> before `reset_batch_size()`, logs when a stale batch is written into an
> already
> offlined and zeroed memcg lruvec, and dumps the non-zero
> `lrugen.nr_pages` slots
> before `lru_gen_exit_memcg()` triggers the warning.
>
> A successful run reports `status=repro_triggered`, and the extracted events
> include a warning like:
>
> WARNING: mm/vmscan.c:5943 at lru_gen_exit_memcg+0x420/0x520
>
> Proposed Fix:
>
> One possible fix direction is to make `reset_batch_size()` skip writing
> back the
> stale delta when the memcg is no longer online. `reset_batch_size()` is
> called
> under `lruvec_lock`, the same lock that `lru_gen_reparent_memcg()` holds
> when it
> zeroes `nr_pages`, so this should avoid committing a batch after
> reparenting has
> completed.
>
> Possible fix direction, not a tested patch:
>
> --- a/mm/vmscan.c
> +++ b/mm/vmscan.c
> @@ -... reset_batch_size() ...
> static void reset_batch_size(struct lru_gen_mm_walk *walk)
> {
> int gen, type, zone;
> struct lruvec *lruvec = walk->lruvec;
> struct lru_gen_folio *lrugen = &lruvec->lrugen;
> + struct mem_cgroup *memcg = lruvec_memcg(lruvec);
>
> walk->batched = 0;
>
> for_each_gen_type_zone(gen, type, zone) {
> enum lru_list lru = type * LRU_INACTIVE_FILE;
> int delta = walk->nr_pages[gen][type][zone];
>
> if (!delta)
> continue;
>
> walk->nr_pages[gen][type][zone] = 0;
> +
> + /*
> + * If the memcg went offline while we were walking page tables,
> + * lru_gen_reparent_memcg() has already zeroed nr_pages and moved
> + * all folios to the parent. Writing our stale batch delta back
> + * would corrupt the offline child and trigger WARN_ON in
> + * lru_gen_exit_memcg(). Discard the delta; the parent lruvec
> + * already owns the pages and accounts for them correctly.
> + */
> + if (memcg && !mem_cgroup_online(memcg))
> + continue;
This check is insufficient, because offline_css() clears the CSS_ONLINE
after ss->css_offline(css). And we can not simple drop the delta.
Thanks,
Qi
> +
> WRITE_ONCE(lrugen->nr_pages[gen][type][zone],
> lrugen->nr_pages[gen][type][zone] + delta);
>
> if (lru_gen_is_active(lruvec, gen))
> lru += LRU_ACTIVE;
> __update_lru_size(lruvec, lru, zone, delta);
> }
> }
>
> Thanks
^ permalink raw reply
* [PATCH v3 0/7] Prepare mutable list iterators to cache cursor state
From: Kaitao Cheng @ 2026-06-22 4:05 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Jens Axboe, Tejun Heo,
Alexander Viro, Christian Brauner, Alexei Starovoitov,
Daniel Borkmann, Andrii Nakryiko, Johannes Weiner, Peter Zijlstra,
Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim,
Thomas Gleixner, Juri Lelli, Vincent Guittot, Paul Moore,
Andy Shevchenko, Paul E. McKenney, Shakeel Butt,
Christian König
Cc: David Howells, Simona Vetter, Randy Dunlap, Luca Ceresoli,
Philipp Stanner, linux-block, linux-kernel, cgroups,
linux-ntfs-dev, linux-fsdevel, io-uring, audit, bpf, netdev,
dri-devel, linux-perf-users, linux-trace-kernel, kexec,
live-patching, linux-modules, linux-crypto, linux-pm, rcu,
sched-ext, linux-mm, virtualization, damon, llvm, chengkaitao
From: chengkaitao <chengkaitao@kylinos.cn>
The list_for_each*_safe() helpers are used when the loop body may remove
the current entry. Their current interface, however, forces every caller
to define a temporary cursor outside the macro and pass it in, even when
the caller never uses that cursor directly. For most call sites this
extra cursor is just boilerplate required by the macro implementation.
This is awkward because the saved next pointer is an internal detail of
the iteration. Callers that only remove or move the current entry do not
need to spell it out.
The _safe() suffix has also caused confusion. Christian Koenig pointed
out that the name is easy to read as a thread-safe variant, especially
for beginners, even though it only means that the iterator keeps enough
state to tolerate removal of the current entry. He suggested _mutable()
as a clearer description of what the loop permits.
Add *_mutable() iterator variants for list, hlist and llist. The new
helpers are variadic and support both forms. In the common case, the
caller omits the temporary cursor and the macro creates a unique internal
cursor with typeof(pos) and __UNIQUE_ID(). If a loop really needs an
explicit temporary cursor, the caller can still pass it and the helper
keeps the existing *_safe() behaviour.
For example, a call site may use the shorter form:
list_for_each_entry_mutable(pos, head, member)
or keep the explicit temporary cursor form:
list_for_each_entry_mutable(pos, tmp, head, member)
The existing *_safe() helpers remain available for compatibility. This
series only converts users in mm, block, kernel, init and io_uring. If
this approach looks acceptable, the remaining users can be converted in
follow-up series.
Changes in v3 (Christian König, Andy Shevchenko):
- Convert safe list walks to mutable iterators
Changes in v2 (Muchun Song, Andy Shevchenko):
- Drop the list_for_each_entry_mutable*() helpers from v1 and make the
cursor change directly in the existing list_for_each_entry*() helpers.
- Open-code special list walks that rely on updating the loop cursor in
the body, preserving their existing traversal semantics.
Link to v2:
https://lore.kernel.org/all/20260609061347.93688-1-kaitao.cheng@linux.dev/
Link to v1:
https://lore.kernel.org/all/20260529082149.76764-1-kaitao.cheng@linux.dev/
Kaitao Cheng (7):
list: Add mutable iterator variants
llist: Add mutable iterator variants
mm: Use mutable list iterators
block: Use mutable list iterators
kernel: Use mutable list iterators
initramfs: Use mutable list iterator
io_uring: Use mutable list iterators
block/bfq-iosched.c | 17 +-
block/blk-cgroup.c | 12 +-
block/blk-flush.c | 4 +-
block/blk-iocost.c | 18 +-
block/blk-mq.c | 8 +-
block/blk-throttle.c | 4 +-
block/kyber-iosched.c | 4 +-
block/partitions/ldm.c | 8 +-
block/sed-opal.c | 4 +-
include/linux/list.h | 269 ++++++++++++++++++++++++----
include/linux/llist.h | 81 +++++++--
init/initramfs.c | 5 +-
io_uring/cancel.c | 6 +-
io_uring/poll.c | 3 +-
io_uring/rw.c | 4 +-
io_uring/timeout.c | 8 +-
io_uring/uring_cmd.c | 3 +-
kernel/audit_tree.c | 4 +-
kernel/audit_watch.c | 16 +-
kernel/auditfilter.c | 4 +-
kernel/auditsc.c | 4 +-
kernel/bpf/arena.c | 10 +-
kernel/bpf/arraymap.c | 8 +-
kernel/bpf/bpf_local_storage.c | 3 +-
kernel/bpf/bpf_lru_list.c | 25 ++-
kernel/bpf/btf.c | 18 +-
kernel/bpf/cgroup.c | 7 +-
kernel/bpf/cpumap.c | 4 +-
kernel/bpf/devmap.c | 10 +-
kernel/bpf/helpers.c | 8 +-
kernel/bpf/local_storage.c | 4 +-
kernel/bpf/memalloc.c | 16 +-
kernel/bpf/offload.c | 8 +-
kernel/bpf/states.c | 4 +-
kernel/bpf/stream.c | 4 +-
kernel/bpf/verifier.c | 6 +-
kernel/cgroup/cgroup-v1.c | 4 +-
kernel/cgroup/cgroup.c | 54 +++---
kernel/cgroup/dmem.c | 12 +-
kernel/cgroup/rdma.c | 8 +-
kernel/events/core.c | 44 +++--
kernel/events/uprobes.c | 12 +-
kernel/exit.c | 8 +-
kernel/fail_function.c | 4 +-
kernel/gcov/clang.c | 4 +-
kernel/irq_work.c | 4 +-
kernel/kexec_core.c | 4 +-
kernel/kprobes.c | 16 +-
kernel/livepatch/core.c | 4 +-
kernel/livepatch/core.h | 4 +-
kernel/liveupdate/kho_block.c | 4 +-
kernel/liveupdate/luo_flb.c | 4 +-
kernel/locking/rwsem.c | 2 +-
kernel/locking/test-ww_mutex.c | 2 +-
kernel/module/main.c | 11 +-
kernel/padata.c | 4 +-
kernel/power/snapshot.c | 8 +-
kernel/power/wakelock.c | 4 +-
kernel/printk/printk.c | 11 +-
kernel/ptrace.c | 4 +-
kernel/rcu/rcutorture.c | 3 +-
kernel/rcu/tasks.h | 9 +-
kernel/rcu/tree.c | 6 +-
kernel/resource.c | 4 +-
kernel/sched/core.c | 4 +-
kernel/sched/ext.c | 22 +--
kernel/sched/fair.c | 28 +--
kernel/sched/topology.c | 4 +-
kernel/sched/wait.c | 4 +-
kernel/seccomp.c | 4 +-
kernel/signal.c | 11 +-
kernel/smp.c | 4 +-
kernel/taskstats.c | 8 +-
kernel/time/clockevents.c | 6 +-
kernel/time/clocksource.c | 4 +-
kernel/time/posix-cpu-timers.c | 4 +-
kernel/time/posix-timers.c | 3 +-
kernel/torture.c | 3 +-
kernel/trace/bpf_trace.c | 4 +-
kernel/trace/ftrace.c | 49 +++--
kernel/trace/ring_buffer.c | 25 ++-
kernel/trace/trace.c | 12 +-
kernel/trace/trace_dynevent.c | 6 +-
kernel/trace/trace_dynevent.h | 5 +-
kernel/trace/trace_events.c | 35 ++--
kernel/trace/trace_events_filter.c | 4 +-
kernel/trace/trace_events_hist.c | 8 +-
kernel/trace/trace_events_trigger.c | 17 +-
kernel/trace/trace_events_user.c | 16 +-
kernel/trace/trace_stat.c | 4 +-
kernel/user-return-notifier.c | 3 +-
kernel/workqueue.c | 16 +-
mm/backing-dev.c | 8 +-
mm/balloon.c | 8 +-
mm/cma.c | 4 +-
mm/compaction.c | 4 +-
mm/damon/core.c | 4 +-
mm/damon/sysfs-schemes.c | 4 +-
mm/dmapool.c | 4 +-
mm/huge_memory.c | 8 +-
mm/hugetlb.c | 56 +++---
mm/hugetlb_vmemmap.c | 16 +-
mm/khugepaged.c | 14 +-
mm/kmemleak.c | 7 +-
mm/ksm.c | 25 +--
mm/list_lru.c | 4 +-
mm/memcontrol-v1.c | 8 +-
mm/memory-failure.c | 12 +-
mm/memory-tiers.c | 4 +-
mm/migrate.c | 23 ++-
mm/mmu_notifier.c | 9 +-
mm/page_alloc.c | 8 +-
mm/page_reporting.c | 2 +-
mm/percpu.c | 11 +-
mm/pgtable-generic.c | 4 +-
mm/rmap.c | 10 +-
mm/shmem.c | 9 +-
mm/slab_common.c | 14 +-
mm/slub.c | 33 ++--
mm/swapfile.c | 4 +-
mm/userfaultfd.c | 12 +-
mm/vmalloc.c | 24 +--
mm/vmscan.c | 7 +-
mm/zsmalloc.c | 4 +-
124 files changed, 875 insertions(+), 681 deletions(-)
--
2.43.0
^ permalink raw reply
* [PATCH v3 1/7] list: Add mutable iterator variants
From: Kaitao Cheng @ 2026-06-22 4:05 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Jens Axboe, Tejun Heo,
Alexander Viro, Christian Brauner, Alexei Starovoitov,
Daniel Borkmann, Andrii Nakryiko, Johannes Weiner, Peter Zijlstra,
Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim,
Thomas Gleixner, Juri Lelli, Vincent Guittot, Paul Moore,
Andy Shevchenko, Paul E. McKenney, Shakeel Butt,
Christian König
Cc: David Howells, Simona Vetter, Randy Dunlap, Luca Ceresoli,
Philipp Stanner, linux-block, linux-kernel, cgroups,
linux-ntfs-dev, linux-fsdevel, io-uring, audit, bpf, netdev,
dri-devel, linux-perf-users, linux-trace-kernel, kexec,
live-patching, linux-modules, linux-crypto, linux-pm, rcu,
sched-ext, linux-mm, virtualization, damon, llvm, Kaitao Cheng
In-Reply-To: <20260622040533.29824-1-kaitao.cheng@linux.dev>
From: Kaitao Cheng <chengkaitao@kylinos.cn>
The list_for_each*_safe() helpers are used when the loop body may
remove the current entry. Their API exposes the temporary cursor at
every call site, even though most users only need it for the iterator
implementation and never reference it in the loop body.
Add *_mutable() variants for list and hlist iteration. The new helpers
support both forms: callers may keep passing an explicit temporary cursor
when they need to inspect or reset it, or omit it and let the helper use
a unique internal cursor.
This makes call sites that only mutate the list through the current entry
less noisy, while keeping the existing *_safe() helpers available for
compatibility.
Signed-off-by: Kaitao Cheng <chengkaitao@kylinos.cn>
---
include/linux/list.h | 269 +++++++++++++++++++++++++++++++++++++------
1 file changed, 231 insertions(+), 38 deletions(-)
diff --git a/include/linux/list.h b/include/linux/list.h
index 09d979976b3b..1081def7cea9 100644
--- a/include/linux/list.h
+++ b/include/linux/list.h
@@ -7,6 +7,7 @@
#include <linux/stddef.h>
#include <linux/poison.h>
#include <linux/const.h>
+#include <linux/args.h>
#include <asm/barrier.h>
@@ -763,28 +764,72 @@ static inline void list_splice_tail_init(struct list_head *list,
#define list_for_each_prev(pos, head) \
for (pos = (head)->prev; !list_is_head(pos, (head)); pos = pos->prev)
-/**
- * list_for_each_safe - iterate over a list safe against removal of list entry
- * @pos: the &struct list_head to use as a loop cursor.
- * @n: another &struct list_head to use as temporary storage
- * @head: the head for your list.
+/*
+ * list_for_each_safe is an old interface, use list_for_each_mutable instead.
*/
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; \
!list_is_head(pos, (head)); \
pos = n, n = pos->next)
+#define __list_for_each_mutable_internal(pos, tmp, head) \
+ for (typeof(pos) tmp = (pos = (head)->next)->next; \
+ !list_is_head(pos, (head)); \
+ pos = tmp, tmp = pos->next)
+
+#define __list_for_each_mutable1(pos, head) \
+ __list_for_each_mutable_internal(pos, __UNIQUE_ID(next), head)
+
+#define __list_for_each_mutable2(pos, next, head) \
+ list_for_each_safe(pos, next, head)
+
/**
- * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
+ * list_for_each_mutable - iterate over a list safe against entry removal
* @pos: the &struct list_head to use as a loop cursor.
- * @n: another &struct list_head to use as temporary storage
- * @head: the head for your list.
+ * @...: either (head) or (next, head)
+ *
+ * next: another &struct list_head to use as optional temporary storage.
+ * The temporary cursor is internal unless explicitly supplied by
+ * the caller.
+ * head: the head for your list.
+ */
+#define list_for_each_mutable(pos, ...) \
+ CONCATENATE(__list_for_each_mutable, COUNT_ARGS(__VA_ARGS__)) \
+ (pos, __VA_ARGS__)
+
+/*
+ * list_for_each_prev_safe is an old interface, use list_for_each_prev_mutable instead.
*/
#define list_for_each_prev_safe(pos, n, head) \
for (pos = (head)->prev, n = pos->prev; \
!list_is_head(pos, (head)); \
pos = n, n = pos->prev)
+#define __list_for_each_prev_mutable_internal(pos, tmp, head) \
+ for (typeof(pos) tmp = (pos = (head)->prev)->prev; \
+ !list_is_head(pos, (head)); \
+ pos = tmp, tmp = pos->prev)
+
+#define __list_for_each_prev_mutable1(pos, head) \
+ __list_for_each_prev_mutable_internal(pos, __UNIQUE_ID(prev), head)
+
+#define __list_for_each_prev_mutable2(pos, prev, head) \
+ list_for_each_prev_safe(pos, prev, head)
+
+/**
+ * list_for_each_prev_mutable - iterate over a list backwards safe against entry removal
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @...: either (head) or (prev, head)
+ *
+ * prev: another &struct list_head to use as optional temporary storage.
+ * The temporary cursor is internal unless explicitly supplied by
+ * the caller.
+ * head: the head for your list.
+ */
+#define list_for_each_prev_mutable(pos, ...) \
+ CONCATENATE(__list_for_each_prev_mutable, COUNT_ARGS(__VA_ARGS__)) \
+ (pos, __VA_ARGS__)
+
/**
* list_count_nodes - count nodes in the list
* @head: the head for your list.
@@ -895,12 +940,8 @@ static inline size_t list_count_nodes(struct list_head *head)
for (; !list_entry_is_head(pos, head, member); \
pos = list_prev_entry(pos, member))
-/**
- * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
- * @pos: the type * to use as a loop cursor.
- * @n: another type * to use as temporary storage
- * @head: the head for your list.
- * @member: the name of the list_head within the struct.
+/*
+ * list_for_each_entry_safe is an old interface, use list_for_each_entry_mutable instead.
*/
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_first_entry(head, typeof(*pos), member), \
@@ -908,15 +949,36 @@ static inline size_t list_count_nodes(struct list_head *head)
!list_entry_is_head(pos, head, member); \
pos = n, n = list_next_entry(n, member))
+#define __list_for_each_entry_mutable_internal(pos, tmp, head, member) \
+ for (typeof(pos) tmp = list_next_entry(pos = \
+ list_first_entry(head, typeof(*pos), member), member); \
+ !list_entry_is_head(pos, head, member); \
+ pos = tmp, tmp = list_next_entry(tmp, member))
+
+#define __list_for_each_entry_mutable2(pos, head, member) \
+ __list_for_each_entry_mutable_internal(pos, __UNIQUE_ID(next), head, member)
+
+#define __list_for_each_entry_mutable3(pos, next, head, member) \
+ list_for_each_entry_safe(pos, next, head, member)
+
/**
- * list_for_each_entry_safe_continue - continue list iteration safe against removal
+ * list_for_each_entry_mutable - iterate over a list safe against entry removal
* @pos: the type * to use as a loop cursor.
- * @n: another type * to use as temporary storage
- * @head: the head for your list.
- * @member: the name of the list_head within the struct.
+ * @...: either (head, member) or (next, head, member)
*
- * Iterate over list of given type, continuing after current point,
- * safe against removal of list entry.
+ * next: another type * to use as optional temporary storage. The
+ * temporary cursor is internal unless explicitly supplied by the
+ * caller.
+ * head: the head for your list.
+ * member: the name of the list_head within the struct.
+ */
+#define list_for_each_entry_mutable(pos, ...) \
+ CONCATENATE(__list_for_each_entry_mutable, COUNT_ARGS(__VA_ARGS__)) \
+ (pos, __VA_ARGS__)
+
+/*
+ * list_for_each_entry_safe_continue is an old interface,
+ * use list_for_each_entry_mutable_continue instead.
*/
#define list_for_each_entry_safe_continue(pos, n, head, member) \
for (pos = list_next_entry(pos, member), \
@@ -924,30 +986,79 @@ static inline size_t list_count_nodes(struct list_head *head)
!list_entry_is_head(pos, head, member); \
pos = n, n = list_next_entry(n, member))
+#define __list_for_each_entry_mutable_continue_internal(pos, tmp, head, member) \
+ for (typeof(pos) tmp = list_next_entry(pos = \
+ list_next_entry(pos, member), member); \
+ !list_entry_is_head(pos, head, member); \
+ pos = tmp, tmp = list_next_entry(tmp, member))
+
+#define __list_for_each_entry_mutable_continue2(pos, head, member) \
+ __list_for_each_entry_mutable_continue_internal(pos, \
+ __UNIQUE_ID(next), head, member)
+
+#define __list_for_each_entry_mutable_continue3(pos, next, head, member) \
+ list_for_each_entry_safe_continue(pos, next, head, member)
+
/**
- * list_for_each_entry_safe_from - iterate over list from current point safe against removal
+ * list_for_each_entry_mutable_continue - continue list iteration safe against removal
* @pos: the type * to use as a loop cursor.
- * @n: another type * to use as temporary storage
- * @head: the head for your list.
- * @member: the name of the list_head within the struct.
+ * @...: either (head, member) or (next, head, member)
*
- * Iterate over list of given type from current point, safe against
- * removal of list entry.
+ * next: another type * to use as optional temporary storage. The
+ * temporary cursor is internal unless explicitly supplied by the
+ * caller.
+ * head: the head for your list.
+ * member: the name of the list_head within the struct.
+ *
+ * Iterate over list of given type, continuing after current point,
+ * safe against removal of list entry.
+ */
+#define list_for_each_entry_mutable_continue(pos, ...) \
+ CONCATENATE(__list_for_each_entry_mutable_continue, \
+ COUNT_ARGS(__VA_ARGS__))(pos, __VA_ARGS__)
+
+/*
+ * list_for_each_entry_safe_from is an old interface,
+ * use list_for_each_entry_mutable_from instead.
*/
#define list_for_each_entry_safe_from(pos, n, head, member) \
for (n = list_next_entry(pos, member); \
!list_entry_is_head(pos, head, member); \
pos = n, n = list_next_entry(n, member))
+#define __list_for_each_entry_mutable_from_internal(pos, tmp, head, member) \
+ for (typeof(pos) tmp = list_next_entry(pos, member); \
+ !list_entry_is_head(pos, head, member); \
+ pos = tmp, tmp = list_next_entry(tmp, member))
+
+#define __list_for_each_entry_mutable_from2(pos, head, member) \
+ __list_for_each_entry_mutable_from_internal(pos, \
+ __UNIQUE_ID(next), head, member)
+
+#define __list_for_each_entry_mutable_from3(pos, next, head, member) \
+ list_for_each_entry_safe_from(pos, next, head, member)
+
/**
- * list_for_each_entry_safe_reverse - iterate backwards over list safe against removal
+ * list_for_each_entry_mutable_from - iterate over list from current point safe against removal
* @pos: the type * to use as a loop cursor.
- * @n: another type * to use as temporary storage
- * @head: the head for your list.
- * @member: the name of the list_head within the struct.
+ * @...: either (head, member) or (next, head, member)
*
- * Iterate backwards over list of given type, safe against removal
- * of list entry.
+ * next: another type * to use as optional temporary storage. The
+ * temporary cursor is internal unless explicitly supplied by the
+ * caller.
+ * head: the head for your list.
+ * member: the name of the list_head within the struct.
+ *
+ * Iterate over list of given type from current point, safe against
+ * removal of list entry.
+ */
+#define list_for_each_entry_mutable_from(pos, ...) \
+ CONCATENATE(__list_for_each_entry_mutable_from, \
+ COUNT_ARGS(__VA_ARGS__))(pos, __VA_ARGS__)
+
+/*
+ * list_for_each_entry_safe_reverse is an old interface,
+ * use list_for_each_entry_mutable_reverse instead.
*/
#define list_for_each_entry_safe_reverse(pos, n, head, member) \
for (pos = list_last_entry(head, typeof(*pos), member), \
@@ -955,6 +1066,37 @@ static inline size_t list_count_nodes(struct list_head *head)
!list_entry_is_head(pos, head, member); \
pos = n, n = list_prev_entry(n, member))
+#define __list_for_each_entry_mutable_reverse_internal(pos, tmp, head, member) \
+ for (typeof(pos) tmp = list_prev_entry(pos = \
+ list_last_entry(head, typeof(*pos), member), member); \
+ !list_entry_is_head(pos, head, member); \
+ pos = tmp, tmp = list_prev_entry(tmp, member))
+
+#define __list_for_each_entry_mutable_reverse2(pos, head, member) \
+ __list_for_each_entry_mutable_reverse_internal(pos, \
+ __UNIQUE_ID(prev), head, member)
+
+#define __list_for_each_entry_mutable_reverse3(pos, prev, head, member) \
+ list_for_each_entry_safe_reverse(pos, prev, head, member)
+
+/**
+ * list_for_each_entry_mutable_reverse - iterate backwards over list safe against removal
+ * @pos: the type * to use as a loop cursor.
+ * @...: either (head, member) or (prev, head, member)
+ *
+ * prev: another type * to use as optional temporary storage. The
+ * temporary cursor is internal unless explicitly supplied by the
+ * caller.
+ * head: the head for your list.
+ * member: the name of the list_head within the struct.
+ *
+ * Iterate backwards over list of given type, safe against removal
+ * of list entry.
+ */
+#define list_for_each_entry_mutable_reverse(pos, ...) \
+ CONCATENATE(__list_for_each_entry_mutable_reverse, \
+ COUNT_ARGS(__VA_ARGS__))(pos, __VA_ARGS__)
+
/**
* list_safe_reset_next - reset a stale list_for_each_entry_safe loop
* @pos: the loop cursor used in the list_for_each_entry_safe loop
@@ -1189,6 +1331,31 @@ static inline void hlist_splice_init(struct hlist_head *from,
for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
pos = n)
+#define __hlist_for_each_mutable_internal(pos, tmp, head) \
+ for (typeof(pos) tmp = (pos = (head)->first) ? pos->next : NULL; \
+ pos; \
+ pos = tmp, tmp = pos ? pos->next : NULL)
+
+#define __hlist_for_each_mutable1(pos, head) \
+ __hlist_for_each_mutable_internal(pos, __UNIQUE_ID(next), head)
+
+#define __hlist_for_each_mutable2(pos, next, head) \
+ hlist_for_each_safe(pos, next, head)
+
+/**
+ * hlist_for_each_mutable - iterate over a hlist safe against entry removal
+ * @pos: the &struct hlist_node to use as a loop cursor.
+ * @...: either (head) or (next, head)
+ *
+ * next: another &struct hlist_node to use as optional temporary storage.
+ * The temporary cursor is internal unless explicitly supplied by
+ * the caller.
+ * head: the head for your hlist.
+ */
+#define hlist_for_each_mutable(pos, ...) \
+ CONCATENATE(__hlist_for_each_mutable, COUNT_ARGS(__VA_ARGS__)) \
+ (pos, __VA_ARGS__)
+
#define hlist_entry_safe(ptr, type, member) \
({ typeof(ptr) ____ptr = (ptr); \
____ptr ? hlist_entry(____ptr, type, member) : NULL; \
@@ -1224,18 +1391,44 @@ static inline void hlist_splice_init(struct hlist_head *from,
for (; pos; \
pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
-/**
- * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
- * @pos: the type * to use as a loop cursor.
- * @n: a &struct hlist_node to use as temporary storage
- * @head: the head for your list.
- * @member: the name of the hlist_node within the struct.
+/*
+ * hlist_for_each_entry_safe is an old interface, use hlist_for_each_entry_mutable instead.
*/
#define hlist_for_each_entry_safe(pos, n, head, member) \
for (pos = hlist_entry_safe((head)->first, typeof(*pos), member);\
pos && ({ n = pos->member.next; 1; }); \
pos = hlist_entry_safe(n, typeof(*pos), member))
+#define __hlist_for_each_entry_mutable_internal(pos, tmp, head, member) \
+ for (struct hlist_node *tmp = (pos = \
+ hlist_entry_safe((head)->first, typeof(*pos), member)) ? \
+ pos->member.next : NULL; \
+ pos; \
+ pos = hlist_entry_safe((tmp), typeof(*pos), member), \
+ tmp = pos ? pos->member.next : NULL)
+
+#define __hlist_for_each_entry_mutable2(pos, head, member) \
+ __hlist_for_each_entry_mutable_internal(pos, \
+ __UNIQUE_ID(next), head, member)
+
+#define __hlist_for_each_entry_mutable3(pos, next, head, member) \
+ hlist_for_each_entry_safe(pos, next, head, member)
+
+/**
+ * hlist_for_each_entry_mutable - iterate over hlist safe against entry removal
+ * @pos: the type * to use as a loop cursor.
+ * @...: either (head, member) or (next, head, member)
+ *
+ * next: a &struct hlist_node to use as optional temporary storage. The
+ * temporary cursor is internal unless explicitly supplied by the
+ * caller.
+ * head: the head for your hlist.
+ * member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_mutable(pos, ...) \
+ CONCATENATE(__hlist_for_each_entry_mutable, \
+ COUNT_ARGS(__VA_ARGS__))(pos, __VA_ARGS__)
+
/**
* hlist_count_nodes - count nodes in the hlist
* @head: the head for your hlist.
--
2.43.0
^ permalink raw reply related
* [PATCH v3 2/7] llist: Add mutable iterator variants
From: Kaitao Cheng @ 2026-06-22 4:05 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Jens Axboe, Tejun Heo,
Alexander Viro, Christian Brauner, Alexei Starovoitov,
Daniel Borkmann, Andrii Nakryiko, Johannes Weiner, Peter Zijlstra,
Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim,
Thomas Gleixner, Juri Lelli, Vincent Guittot, Paul Moore,
Andy Shevchenko, Paul E. McKenney, Shakeel Butt,
Christian König
Cc: David Howells, Simona Vetter, Randy Dunlap, Luca Ceresoli,
Philipp Stanner, linux-block, linux-kernel, cgroups,
linux-ntfs-dev, linux-fsdevel, io-uring, audit, bpf, netdev,
dri-devel, linux-perf-users, linux-trace-kernel, kexec,
live-patching, linux-modules, linux-crypto, linux-pm, rcu,
sched-ext, linux-mm, virtualization, damon, llvm, Kaitao Cheng
In-Reply-To: <20260622040533.29824-1-kaitao.cheng@linux.dev>
From: Kaitao Cheng <chengkaitao@kylinos.cn>
llist_for_each_safe() and llist_for_each_entry_safe() require callers to
provide a temporary cursor even when the cursor is only needed by the
iterator itself. This makes call sites noisier than necessary for the
common case where the loop body may remove the current entry but does
not otherwise inspect the saved next pointer.
Add llist_for_each_mutable() and llist_for_each_entry_mutable() variants
that support both forms. Callers may omit the temporary cursor and let
the helper create an internal unique cursor, or keep passing an explicit
cursor when the loop needs to inspect or reset it.
Keep the existing safe helpers as compatibility wrappers so current users
continue to build unchanged while new code can use the shorter mutable
form.
Signed-off-by: Kaitao Cheng <chengkaitao@kylinos.cn>
---
include/linux/llist.h | 81 ++++++++++++++++++++++++++++++++++---------
1 file changed, 65 insertions(+), 16 deletions(-)
diff --git a/include/linux/llist.h b/include/linux/llist.h
index 8846b7709669..1c6f12411d5e 100644
--- a/include/linux/llist.h
+++ b/include/linux/llist.h
@@ -49,6 +49,7 @@
*/
#include <linux/atomic.h>
+#include <linux/args.h>
#include <linux/container_of.h>
#include <linux/stddef.h>
#include <linux/types.h>
@@ -143,12 +144,33 @@ static inline bool llist_on_list(const struct llist_node *node)
#define llist_for_each(pos, node) \
for ((pos) = (node); pos; (pos) = (pos)->next)
+/*
+ * llist_for_each_safe is an old interface, use llist_for_each_mutable instead.
+ */
+#define llist_for_each_safe(pos, n, node) \
+ for ((pos) = (node); (pos) && ((n) = (pos)->next, true); (pos) = (n))
+
+#define __llist_for_each_mutable_internal(pos, tmp, node) \
+ for (typeof(pos) tmp = ((pos) = (node)) ? (pos)->next : NULL; \
+ (pos); \
+ (pos) = tmp, tmp = (pos) ? (pos)->next : NULL)
+
+#define __llist_for_each_mutable1(pos, node) \
+ __llist_for_each_mutable_internal(pos, __UNIQUE_ID(next), node)
+
+#define __llist_for_each_mutable2(pos, next, node) \
+ llist_for_each_safe(pos, next, node)
+
/**
- * llist_for_each_safe - iterate over some deleted entries of a lock-less list
- * safe against removal of list entry
+ * llist_for_each_mutable - iterate over some deleted entries of a lock-less list
+ * safe against removal of list entry
* @pos: the &struct llist_node to use as a loop cursor
- * @n: another &struct llist_node to use as temporary storage
- * @node: the first entry of deleted list entries
+ * @...: either (node) or (next, node)
+ *
+ * next: another &struct llist_node to use as optional temporary storage.
+ * The temporary cursor is internal unless explicitly supplied by
+ * the caller.
+ * node: the first entry of deleted list entries
*
* In general, some entries of the lock-less list can be traversed
* safely only after being deleted from list, so start with an entry
@@ -159,8 +181,9 @@ static inline bool llist_on_list(const struct llist_node *node)
* you want to traverse from the oldest to the newest, you must
* reverse the order by yourself before traversing.
*/
-#define llist_for_each_safe(pos, n, node) \
- for ((pos) = (node); (pos) && ((n) = (pos)->next, true); (pos) = (n))
+#define llist_for_each_mutable(pos, ...) \
+ CONCATENATE(__llist_for_each_mutable, COUNT_ARGS(__VA_ARGS__)) \
+ (pos, __VA_ARGS__)
/**
* llist_for_each_entry - iterate over some deleted entries of lock-less list of given type
@@ -182,13 +205,41 @@ static inline bool llist_on_list(const struct llist_node *node)
member_address_is_nonnull(pos, member); \
(pos) = llist_entry((pos)->member.next, typeof(*(pos)), member))
+/*
+ * llist_for_each_entry_safe is an old interface, use llist_for_each_entry_mutable instead.
+ */
+#define llist_for_each_entry_safe(pos, n, node, member) \
+ for (pos = llist_entry((node), typeof(*pos), member); \
+ member_address_is_nonnull(pos, member) && \
+ (n = llist_entry(pos->member.next, typeof(*n), member), true); \
+ pos = n)
+
+#define __llist_for_each_entry_mutable_internal(pos, tmp, node, member) \
+ for (typeof(pos) tmp = ((pos) = llist_entry((node), typeof(*pos), member), \
+ member_address_is_nonnull(pos, member) ? \
+ llist_entry((pos)->member.next, typeof(*pos), member) : NULL); \
+ member_address_is_nonnull(pos, member); \
+ (pos) = tmp, tmp = member_address_is_nonnull(pos, member) ? \
+ llist_entry((pos)->member.next, typeof(*pos), member) : NULL)
+
+#define __llist_for_each_entry_mutable2(pos, node, member) \
+ __llist_for_each_entry_mutable_internal(pos, __UNIQUE_ID(next), node, member)
+
+#define __llist_for_each_entry_mutable3(pos, next, node, member) \
+ llist_for_each_entry_safe(pos, next, node, member)
+
/**
- * llist_for_each_entry_safe - iterate over some deleted entries of lock-less list of given type
- * safe against removal of list entry
+ * llist_for_each_entry_mutable - iterate over some deleted entries of
+ * lock-less list of given type safe against
+ * removal of list entry
* @pos: the type * to use as a loop cursor.
- * @n: another type * to use as temporary storage
- * @node: the first entry of deleted list entries.
- * @member: the name of the llist_node with the struct.
+ * @...: either (node, member) or (next, node, member)
+ *
+ * next: another type * to use as optional temporary storage. The
+ * temporary cursor is internal unless explicitly supplied by the
+ * caller.
+ * node: the first entry of deleted list entries.
+ * member: the name of the llist_node with the struct.
*
* In general, some entries of the lock-less list can be traversed
* safely only after being removed from list, so start with an entry
@@ -199,11 +250,9 @@ static inline bool llist_on_list(const struct llist_node *node)
* you want to traverse from the oldest to the newest, you must
* reverse the order by yourself before traversing.
*/
-#define llist_for_each_entry_safe(pos, n, node, member) \
- for (pos = llist_entry((node), typeof(*pos), member); \
- member_address_is_nonnull(pos, member) && \
- (n = llist_entry(pos->member.next, typeof(*n), member), true); \
- pos = n)
+#define llist_for_each_entry_mutable(pos, ...) \
+ CONCATENATE(__llist_for_each_entry_mutable, \
+ COUNT_ARGS(__VA_ARGS__))(pos, __VA_ARGS__)
/**
* llist_empty - tests whether a lock-less list is empty
--
2.43.0
^ permalink raw reply related
* [PATCH v3 3/7] mm: Use mutable list iterators
From: Kaitao Cheng @ 2026-06-22 4:15 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, SeongJae Park, Muchun Song,
Oscar Salvador, Catalin Marinas, Dave Chinner, Shakeel Butt,
Miaohe Lin, Dennis Zhou, Tejun Heo, Christoph Lameter,
Hugh Dickins, Chris Li, Kairui Song, Uladzislau Rezki,
Minchan Kim, Sergey Senozhatsky
Cc: linux-mm, linux-kernel, virtualization, damon, cgroups,
Kaitao Cheng
In-Reply-To: <20260622040533.29824-1-kaitao.cheng@linux.dev>
From: Kaitao Cheng <chengkaitao@kylinos.cn>
The safe list iterators expose a temporary cursor at every call site,
even when the cursor is only needed by the iterator itself. The mutable
iterator variants keep the removal-safe traversal semantics while hiding
that temporary cursor from callers that do not need it.
Convert mm users of the list, hlist and llist safe iterators to the new
mutable helpers. Drop the temporary cursor variables where the loop does
not inspect or reset them, and keep the explicit cursor at the few sites
that rely on it across lock drops or after the loop.
This is a mechanical cleanup with no intended change in traversal order
or list mutation behavior.
Signed-off-by: Kaitao Cheng <chengkaitao@kylinos.cn>
---
mm/backing-dev.c | 8 +++---
mm/balloon.c | 8 +++---
mm/cma.c | 4 +--
mm/compaction.c | 4 +--
mm/damon/core.c | 4 +--
mm/damon/sysfs-schemes.c | 4 +--
mm/dmapool.c | 4 +--
mm/huge_memory.c | 8 +++---
mm/hugetlb.c | 56 ++++++++++++++++++++--------------------
mm/hugetlb_vmemmap.c | 16 ++++++------
mm/khugepaged.c | 14 +++++-----
mm/kmemleak.c | 7 +++--
mm/ksm.c | 25 +++++++-----------
mm/list_lru.c | 4 +--
mm/memcontrol-v1.c | 8 +++---
mm/memory-failure.c | 12 ++++-----
mm/memory-tiers.c | 4 +--
mm/migrate.c | 23 ++++++++---------
mm/mmu_notifier.c | 9 +++----
mm/page_alloc.c | 8 +++---
mm/page_reporting.c | 2 +-
mm/percpu.c | 11 ++++----
mm/pgtable-generic.c | 4 +--
mm/rmap.c | 10 +++----
mm/shmem.c | 9 ++++---
mm/slab_common.c | 14 +++++-----
mm/slub.c | 33 ++++++++++++-----------
mm/swapfile.c | 4 +--
mm/userfaultfd.c | 12 ++++-----
mm/vmalloc.c | 24 ++++++++---------
mm/vmscan.c | 7 +++--
mm/zsmalloc.c | 4 +--
32 files changed, 175 insertions(+), 189 deletions(-)
diff --git a/mm/backing-dev.c b/mm/backing-dev.c
index cecbcf9060a6..944b9cc7a424 100644
--- a/mm/backing-dev.c
+++ b/mm/backing-dev.c
@@ -932,10 +932,10 @@ static void cleanup_offline_cgwbs_workfn(struct work_struct *work)
void wb_memcg_offline(struct mem_cgroup *memcg)
{
struct list_head *memcg_cgwb_list = &memcg->cgwb_list;
- struct bdi_writeback *wb, *next;
+ struct bdi_writeback *wb;
spin_lock_irq(&cgwb_lock);
- list_for_each_entry_safe(wb, next, memcg_cgwb_list, memcg_node)
+ list_for_each_entry_mutable(wb, memcg_cgwb_list, memcg_node)
cgwb_kill(wb);
memcg_cgwb_list->next = NULL; /* prevent new wb's */
spin_unlock_irq(&cgwb_lock);
@@ -951,11 +951,11 @@ void wb_memcg_offline(struct mem_cgroup *memcg)
*/
void wb_blkcg_offline(struct cgroup_subsys_state *css)
{
- struct bdi_writeback *wb, *next;
+ struct bdi_writeback *wb;
struct list_head *list = blkcg_get_cgwb_list(css);
spin_lock_irq(&cgwb_lock);
- list_for_each_entry_safe(wb, next, list, blkcg_node)
+ list_for_each_entry_mutable(wb, list, blkcg_node)
cgwb_kill(wb);
list->next = NULL; /* prevent new wb's */
spin_unlock_irq(&cgwb_lock);
diff --git a/mm/balloon.c b/mm/balloon.c
index 96a8f1e20bc6..74a7c411b244 100644
--- a/mm/balloon.c
+++ b/mm/balloon.c
@@ -75,12 +75,12 @@ static void balloon_page_enqueue_one(struct balloon_dev_info *b_dev_info,
size_t balloon_page_list_enqueue(struct balloon_dev_info *b_dev_info,
struct list_head *pages)
{
- struct page *page, *tmp;
+ struct page *page;
unsigned long flags;
size_t n_pages = 0;
spin_lock_irqsave(&balloon_pages_lock, flags);
- list_for_each_entry_safe(page, tmp, pages, lru) {
+ list_for_each_entry_mutable(page, pages, lru) {
list_del(&page->lru);
balloon_page_enqueue_one(b_dev_info, page);
n_pages++;
@@ -111,12 +111,12 @@ EXPORT_SYMBOL_GPL(balloon_page_list_enqueue);
size_t balloon_page_list_dequeue(struct balloon_dev_info *b_dev_info,
struct list_head *pages, size_t n_req_pages)
{
- struct page *page, *tmp;
+ struct page *page;
unsigned long flags;
size_t n_pages = 0;
spin_lock_irqsave(&balloon_pages_lock, flags);
- list_for_each_entry_safe(page, tmp, &b_dev_info->pages, lru) {
+ list_for_each_entry_mutable(page, &b_dev_info->pages, lru) {
if (n_pages == n_req_pages)
break;
list_del(&page->lru);
diff --git a/mm/cma.c b/mm/cma.c
index a13ce4999b39..2c6543fe530e 100644
--- a/mm/cma.c
+++ b/mm/cma.c
@@ -539,7 +539,7 @@ int __init cma_declare_contiguous_multi(phys_addr_t total_size,
struct cma_memrange *cmrp;
LIST_HEAD(ranges);
LIST_HEAD(final_ranges);
- struct list_head *mp, *next;
+ struct list_head *mp;
int ret, nr = 1;
u64 i;
struct cma *cma;
@@ -648,7 +648,7 @@ int __init cma_declare_contiguous_multi(phys_addr_t total_size,
* want to mimic a bottom-up memblock allocation.
*/
sizesum = 0;
- list_for_each_safe(mp, next, &ranges) {
+ list_for_each_mutable(mp, &ranges) {
mlp = list_entry(mp, struct cma_init_memrange, list);
list_del(mp);
list_insert_sorted(&final_ranges, mlp, basecmp);
diff --git a/mm/compaction.c b/mm/compaction.c
index b776f35ad020..1734f7978983 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -94,9 +94,9 @@ static unsigned long release_free_list(struct list_head *freepages)
unsigned long high_pfn = 0;
for (order = 0; order < NR_PAGE_ORDERS; order++) {
- struct page *page, *next;
+ struct page *page;
- list_for_each_entry_safe(page, next, &freepages[order], lru) {
+ list_for_each_entry_mutable(page, &freepages[order], lru) {
unsigned long pfn = page_to_pfn(page);
list_del(&page->lru);
diff --git a/mm/damon/core.c b/mm/damon/core.c
index 7e4b9affc5b0..bb1f4466f7af 100644
--- a/mm/damon/core.c
+++ b/mm/damon/core.c
@@ -3394,7 +3394,7 @@ static void damon_verify_ctx(struct damon_ctx *c)
*/
static void kdamond_call(struct damon_ctx *ctx, bool cancel)
{
- struct damon_call_control *control, *next;
+ struct damon_call_control *control;
LIST_HEAD(controls);
damon_verify_ctx(ctx);
@@ -3403,7 +3403,7 @@ static void kdamond_call(struct damon_ctx *ctx, bool cancel)
list_splice_tail_init(&ctx->call_controls, &controls);
mutex_unlock(&ctx->call_controls_lock);
- list_for_each_entry_safe(control, next, &controls, list) {
+ list_for_each_entry_mutable(control, &controls, list) {
if (!control->repeat || cancel)
list_del(&control->list);
diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c
index 329cfd0bbe9f..701b1947bad4 100644
--- a/mm/damon/sysfs-schemes.c
+++ b/mm/damon/sysfs-schemes.c
@@ -329,9 +329,9 @@ static ssize_t total_bytes_show(struct kobject *kobj,
static void damon_sysfs_scheme_regions_rm_dirs(
struct damon_sysfs_scheme_regions *regions)
{
- struct damon_sysfs_scheme_region *r, *next;
+ struct damon_sysfs_scheme_region *r;
- list_for_each_entry_safe(r, next, ®ions->regions_list, list) {
+ list_for_each_entry_mutable(r, ®ions->regions_list, list) {
damos_sysfs_region_rm_dirs(r);
list_del(&r->list);
kobject_put(&r->kobj);
diff --git a/mm/dmapool.c b/mm/dmapool.c
index 5d8af6e29127..226b505ace23 100644
--- a/mm/dmapool.c
+++ b/mm/dmapool.c
@@ -362,7 +362,7 @@ static struct dma_page *pool_alloc_page(struct dma_pool *pool, gfp_t mem_flags)
*/
void dma_pool_destroy(struct dma_pool *pool)
{
- struct dma_page *page, *tmp;
+ struct dma_page *page;
bool empty, busy = false;
if (unlikely(!pool))
@@ -382,7 +382,7 @@ void dma_pool_destroy(struct dma_pool *pool)
busy = true;
}
- list_for_each_entry_safe(page, tmp, &pool->page_list, page_list) {
+ list_for_each_entry_mutable(page, &pool->page_list, page_list) {
if (!busy)
dma_free_coherent(pool->dev, pool->allocation,
page->vaddr, page->dma);
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 2bccb0a53a0a..39d604f0876d 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -924,9 +924,9 @@ static int __init hugepage_init_sysfs(struct kobject **hugepage_kobj)
static void __init hugepage_exit_sysfs(struct kobject *hugepage_kobj)
{
- struct thpsize *thpsize, *tmp;
+ struct thpsize *thpsize;
- list_for_each_entry_safe(thpsize, tmp, &thpsize_list, node) {
+ list_for_each_entry_mutable(thpsize, &thpsize_list, node) {
list_del(&thpsize->node);
kobject_put(&thpsize->kobj);
}
@@ -4462,14 +4462,14 @@ static unsigned long deferred_split_scan(struct shrinker *shrink,
struct shrink_control *sc)
{
LIST_HEAD(dispose);
- struct folio *folio, *next;
+ struct folio *folio;
int split = 0;
unsigned long isolated;
isolated = list_lru_shrink_walk_irq(&deferred_split_lru, sc,
deferred_split_isolate, &dispose);
- list_for_each_entry_safe(folio, next, &dispose, _deferred_list) {
+ list_for_each_entry_mutable(folio, &dispose, _deferred_list) {
bool did_split = false;
bool underused = false;
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 571212b80835..765552a56086 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -598,7 +598,7 @@ static long add_reservation_in_range(struct resv_map *resv, long f, long t,
long add = 0;
struct list_head *head = &resv->regions;
long last_accounted_offset = f;
- struct file_region *iter, *trg = NULL;
+ struct file_region *iter;
struct list_head *rg = NULL;
if (regions_needed)
@@ -608,7 +608,7 @@ static long add_reservation_in_range(struct resv_map *resv, long f, long t,
* [last_accounted_offset, iter->from), at every iteration, with some
* bounds checking.
*/
- list_for_each_entry_safe(iter, trg, head, link) {
+ list_for_each_entry_mutable(iter, head, link) {
/* Skip irrelevant regions that start before our range. */
if (iter->from < f) {
/* If this region ends after the last accounted offset,
@@ -700,7 +700,7 @@ static int allocate_file_region_entries(struct resv_map *resv,
return 0;
out_of_memory:
- list_for_each_entry_safe(rg, trg, &allocated_regions, link) {
+ list_for_each_entry_mutable(rg, &allocated_regions, link) {
list_del(&rg->link);
kfree(rg);
}
@@ -853,13 +853,13 @@ static void region_abort(struct resv_map *resv, long f, long t,
static long region_del(struct resv_map *resv, long f, long t)
{
struct list_head *head = &resv->regions;
- struct file_region *rg, *trg;
+ struct file_region *rg;
struct file_region *nrg = NULL;
long del = 0;
retry:
spin_lock(&resv->lock);
- list_for_each_entry_safe(rg, trg, head, link) {
+ list_for_each_entry_mutable(rg, head, link) {
/*
* Skip regions before the range to be deleted. file_region
* ranges are normally of the form [from, to). However, there
@@ -1109,13 +1109,13 @@ void resv_map_release(struct kref *ref)
{
struct resv_map *resv_map = container_of(ref, struct resv_map, refs);
struct list_head *head = &resv_map->region_cache;
- struct file_region *rg, *trg;
+ struct file_region *rg;
/* Clear out any active regions before we release the map. */
region_del(resv_map, 0, LONG_MAX);
/* ... and any entries left in the cache */
- list_for_each_entry_safe(rg, trg, head, link) {
+ list_for_each_entry_mutable(rg, head, link) {
list_del(&rg->link);
kfree(rg);
}
@@ -1582,7 +1582,7 @@ static void bulk_vmemmap_restore_error(struct hstate *h,
struct list_head *folio_list,
struct list_head *non_hvo_folios)
{
- struct folio *folio, *t_folio;
+ struct folio *folio;
if (!list_empty(non_hvo_folios)) {
/*
@@ -1592,7 +1592,7 @@ static void bulk_vmemmap_restore_error(struct hstate *h,
* hugetlb pages with vmemmap we will free up memory so that we
* can allocate vmemmap for more hugetlb pages.
*/
- list_for_each_entry_safe(folio, t_folio, non_hvo_folios, lru) {
+ list_for_each_entry_mutable(folio, non_hvo_folios, lru) {
list_del(&folio->lru);
spin_lock_irq(&hugetlb_lock);
__folio_clear_hugetlb(folio);
@@ -1611,7 +1611,7 @@ static void bulk_vmemmap_restore_error(struct hstate *h,
* If are able to restore vmemmap and free one hugetlb page, we
* quit processing the list to retry the bulk operation.
*/
- list_for_each_entry_safe(folio, t_folio, folio_list, lru)
+ list_for_each_entry_mutable(folio, folio_list, lru)
if (hugetlb_vmemmap_restore_folio(h, folio)) {
list_del(&folio->lru);
spin_lock_irq(&hugetlb_lock);
@@ -1633,7 +1633,7 @@ static void update_and_free_pages_bulk(struct hstate *h,
struct list_head *folio_list)
{
long ret;
- struct folio *folio, *t_folio;
+ struct folio *folio;
LIST_HEAD(non_hvo_folios);
/*
@@ -1664,7 +1664,7 @@ static void update_and_free_pages_bulk(struct hstate *h,
spin_unlock_irq(&hugetlb_lock);
}
- list_for_each_entry_safe(folio, t_folio, &non_hvo_folios, lru) {
+ list_for_each_entry_mutable(folio, &non_hvo_folios, lru) {
update_and_free_hugetlb_folio(h, folio, false);
cond_resched();
}
@@ -1875,14 +1875,14 @@ void prep_and_add_allocated_folios(struct hstate *h,
struct list_head *folio_list)
{
unsigned long flags;
- struct folio *folio, *tmp_f;
+ struct folio *folio;
/* Send list for bulk vmemmap optimization processing */
hugetlb_vmemmap_optimize_folios(h, folio_list);
/* Add all new pool pages to free lists in one lock cycle */
spin_lock_irqsave(&hugetlb_lock, flags);
- list_for_each_entry_safe(folio, tmp_f, folio_list, lru) {
+ list_for_each_entry_mutable(folio, folio_list, lru) {
account_new_hugetlb_folio(h, folio);
enqueue_hugetlb_folio(h, folio);
}
@@ -2246,7 +2246,7 @@ static int gather_surplus_pages(struct hstate *h, long delta)
__must_hold(&hugetlb_lock)
{
LIST_HEAD(surplus_list);
- struct folio *folio, *tmp;
+ struct folio *folio;
int ret;
long i;
long needed, allocated;
@@ -2319,7 +2319,7 @@ static int gather_surplus_pages(struct hstate *h, long delta)
ret = 0;
/* Free the needed pages to the hugetlb pool */
- list_for_each_entry_safe(folio, tmp, &surplus_list, lru) {
+ list_for_each_entry_mutable(folio, &surplus_list, lru) {
if ((--needed) < 0)
break;
/* Add the page to the hugetlb allocator */
@@ -2332,7 +2332,7 @@ static int gather_surplus_pages(struct hstate *h, long delta)
* Free unnecessary surplus pages to the buddy allocator.
* Pages have no ref count, call free_huge_folio directly.
*/
- list_for_each_entry_safe(folio, tmp, &surplus_list, lru)
+ list_for_each_entry_mutable(folio, &surplus_list, lru)
free_huge_folio(folio);
spin_lock_irq(&hugetlb_lock);
@@ -3197,12 +3197,12 @@ static void __init prep_and_add_bootmem_folios(struct hstate *h,
struct list_head *folio_list)
{
unsigned long flags;
- struct folio *folio, *tmp_f;
+ struct folio *folio;
/* Send list for bulk vmemmap optimization processing */
hugetlb_vmemmap_optimize_bootmem_folios(h, folio_list);
- list_for_each_entry_safe(folio, tmp_f, folio_list, lru) {
+ list_for_each_entry_mutable(folio, folio_list, lru) {
if (!folio_test_hugetlb_vmemmap_optimized(folio)) {
/*
* If HVO fails, initialize all tail struct pages
@@ -3281,10 +3281,10 @@ static void __init hugetlb_bootmem_free_invalid_page(int nid, struct page *page,
static void __init gather_bootmem_prealloc_node(unsigned long nid)
{
LIST_HEAD(folio_list);
- struct huge_bootmem_page *m, *tm;
+ struct huge_bootmem_page *m;
struct hstate *h = NULL, *prev_h = NULL;
- list_for_each_entry_safe(m, tm, &huge_boot_pages[nid], list) {
+ list_for_each_entry_mutable(m, &huge_boot_pages[nid], list) {
struct page *page = virt_to_page(m);
struct folio *folio = (void *)page;
@@ -3669,9 +3669,9 @@ static void try_to_free_low(struct hstate *h, unsigned long count,
* Collect pages to be freed on a list, and free after dropping lock
*/
for_each_node_mask(i, *nodes_allowed) {
- struct folio *folio, *next;
+ struct folio *folio;
struct list_head *freel = &h->hugepage_freelists[i];
- list_for_each_entry_safe(folio, next, freel, lru) {
+ list_for_each_entry_mutable(folio, freel, lru) {
if (count >= h->nr_huge_pages)
goto out;
if (folio_test_highmem(folio))
@@ -3920,7 +3920,7 @@ static long demote_free_hugetlb_folios(struct hstate *src, struct hstate *dst,
struct list_head *src_list)
{
long rc;
- struct folio *folio, *next;
+ struct folio *folio;
LIST_HEAD(dst_list);
LIST_HEAD(ret_list);
@@ -3937,7 +3937,7 @@ static long demote_free_hugetlb_folios(struct hstate *src, struct hstate *dst,
*/
mutex_lock(&dst->resize_lock);
- list_for_each_entry_safe(folio, next, src_list, lru) {
+ list_for_each_entry_mutable(folio, src_list, lru) {
int i;
bool cma;
@@ -3995,9 +3995,9 @@ long demote_pool_huge_page(struct hstate *src, nodemask_t *nodes_allowed,
for_each_node_mask_to_free(src, nr_nodes, node, nodes_allowed) {
LIST_HEAD(list);
- struct folio *folio, *next;
+ struct folio *folio;
- list_for_each_entry_safe(folio, next, &src->hugepage_freelists[node], lru) {
+ list_for_each_entry_mutable(folio, &src->hugepage_freelists[node], lru) {
if (folio_test_hwpoison(folio))
continue;
@@ -4014,7 +4014,7 @@ long demote_pool_huge_page(struct hstate *src, nodemask_t *nodes_allowed,
spin_lock_irq(&hugetlb_lock);
- list_for_each_entry_safe(folio, next, &list, lru) {
+ list_for_each_entry_mutable(folio, &list, lru) {
list_del(&folio->lru);
add_hugetlb_folio(src, folio, false);
diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
index 133b46dfb09f..88552d60ae60 100644
--- a/mm/hugetlb_vmemmap.c
+++ b/mm/hugetlb_vmemmap.c
@@ -193,9 +193,9 @@ static inline void free_vmemmap_page(struct page *page)
/* Free a list of the vmemmap pages */
static void free_vmemmap_page_list(struct list_head *list)
{
- struct page *page, *next;
+ struct page *page;
- list_for_each_entry_safe(page, next, list, lru)
+ list_for_each_entry_mutable(page, list, lru)
free_vmemmap_page(page);
}
@@ -339,7 +339,7 @@ static int alloc_vmemmap_page_list(unsigned long start, unsigned long end,
gfp_t gfp_mask = GFP_KERNEL | __GFP_RETRY_MAYFAIL;
unsigned long nr_pages = (end - start) >> PAGE_SHIFT;
int nid = page_to_nid((struct page *)start);
- struct page *page, *next;
+ struct page *page;
int i;
for (i = 0; i < nr_pages; i++) {
@@ -352,7 +352,7 @@ static int alloc_vmemmap_page_list(unsigned long start, unsigned long end,
return 0;
out:
- list_for_each_entry_safe(page, next, list, lru)
+ list_for_each_entry_mutable(page, list, lru)
__free_page(page);
return -ENOMEM;
}
@@ -454,12 +454,12 @@ long hugetlb_vmemmap_restore_folios(const struct hstate *h,
struct list_head *folio_list,
struct list_head *non_hvo_folios)
{
- struct folio *folio, *t_folio;
+ struct folio *folio;
long restored = 0;
long ret = 0;
unsigned long flags = VMEMMAP_REMAP_NO_TLB_FLUSH;
- list_for_each_entry_safe(folio, t_folio, folio_list, lru) {
+ list_for_each_entry_mutable(folio, folio_list, lru) {
if (folio_test_hugetlb_vmemmap_optimized(folio)) {
ret = __hugetlb_vmemmap_restore_folio(h, folio, flags);
if (ret)
@@ -800,7 +800,7 @@ static struct zone *pfn_to_zone(unsigned nid, unsigned long pfn)
void __init hugetlb_vmemmap_init_late(int nid)
{
- struct huge_bootmem_page *m, *tm;
+ struct huge_bootmem_page *m;
unsigned long phys, nr_pages, start, end;
unsigned long pfn, nr_mmap;
struct zone *zone = NULL;
@@ -810,7 +810,7 @@ void __init hugetlb_vmemmap_init_late(int nid)
if (!READ_ONCE(vmemmap_optimize_enabled))
return;
- list_for_each_entry_safe(m, tm, &huge_boot_pages[nid], list) {
+ list_for_each_entry_mutable(m, &huge_boot_pages[nid], list) {
if (!(m->flags & HUGE_BOOTMEM_HVO))
continue;
diff --git a/mm/khugepaged.c b/mm/khugepaged.c
index 617bca76db49..66a1d72b5cb8 100644
--- a/mm/khugepaged.c
+++ b/mm/khugepaged.c
@@ -640,7 +640,7 @@ static void release_pte_folio(struct folio *folio)
static void release_pte_pages(pte_t *pte, pte_t *_pte,
struct list_head *compound_pagelist)
{
- struct folio *folio, *tmp;
+ struct folio *folio;
while (--_pte >= pte) {
pte_t pteval = ptep_get(_pte);
@@ -658,7 +658,7 @@ static void release_pte_pages(pte_t *pte, pte_t *_pte,
release_pte_folio(folio);
}
- list_for_each_entry_safe(folio, tmp, compound_pagelist, lru) {
+ list_for_each_entry_mutable(folio, compound_pagelist, lru) {
list_del(&folio->lru);
release_pte_folio(folio);
}
@@ -835,7 +835,7 @@ static void __collapse_huge_page_copy_succeeded(pte_t *pte,
{
const unsigned long nr_pages = 1UL << order;
unsigned long end = address + (PAGE_SIZE * nr_pages);
- struct folio *src, *tmp;
+ struct folio *src;
pte_t pteval;
pte_t *_pte;
unsigned int nr_ptes;
@@ -882,7 +882,7 @@ static void __collapse_huge_page_copy_succeeded(pte_t *pte,
}
}
- list_for_each_entry_safe(src, tmp, compound_pagelist, lru) {
+ list_for_each_entry_mutable(src, compound_pagelist, lru) {
list_del(&src->lru);
node_stat_sub_folio(src, NR_ISOLATED_ANON +
folio_is_file_lru(src));
@@ -2244,7 +2244,7 @@ static enum scan_result collapse_file(struct mm_struct *mm, unsigned long addr,
{
struct address_space *mapping = file->f_mapping;
struct page *dst;
- struct folio *folio, *tmp, *new_folio;
+ struct folio *folio, *new_folio;
pgoff_t index = 0, end = start + HPAGE_PMD_NR;
LIST_HEAD(pagelist);
XA_STATE_ORDER(xas, &mapping->i_pages, start, HPAGE_PMD_ORDER);
@@ -2629,7 +2629,7 @@ static enum scan_result collapse_file(struct mm_struct *mm, unsigned long addr,
/*
* The collapse has succeeded, so free the old folios.
*/
- list_for_each_entry_safe(folio, tmp, &pagelist, lru) {
+ list_for_each_entry_mutable(folio, &pagelist, lru) {
list_del(&folio->lru);
lruvec_stat_mod_folio(folio, NR_FILE_PAGES,
-folio_nr_pages(folio));
@@ -2654,7 +2654,7 @@ static enum scan_result collapse_file(struct mm_struct *mm, unsigned long addr,
shmem_uncharge(mapping->host, nr_none);
}
- list_for_each_entry_safe(folio, tmp, &pagelist, lru) {
+ list_for_each_entry_mutable(folio, &pagelist, lru) {
list_del(&folio->lru);
folio_unlock(folio);
folio_putback_lru(folio);
diff --git a/mm/kmemleak.c b/mm/kmemleak.c
index 7c7ba17ce7af..0c0265f7b19f 100644
--- a/mm/kmemleak.c
+++ b/mm/kmemleak.c
@@ -537,7 +537,6 @@ static void mem_pool_free(struct kmemleak_object *object)
*/
static void free_object_rcu(struct rcu_head *rcu)
{
- struct hlist_node *tmp;
struct kmemleak_scan_area *area;
struct kmemleak_object *object =
container_of(rcu, struct kmemleak_object, rcu);
@@ -546,7 +545,7 @@ static void free_object_rcu(struct rcu_head *rcu)
* Once use_count is 0 (guaranteed by put_object), there is no other
* code accessing this object, hence no need for locking.
*/
- hlist_for_each_entry_safe(area, tmp, &object->area_list, node) {
+ hlist_for_each_entry_mutable(area, &object->area_list, node) {
hlist_del(&area->node);
kmem_cache_free(scan_area_cache, area);
}
@@ -2324,14 +2323,14 @@ static const struct file_operations kmemleak_fops = {
static void __kmemleak_do_cleanup(void)
{
- struct kmemleak_object *object, *tmp;
+ struct kmemleak_object *object;
unsigned int cnt = 0;
/*
* Kmemleak has already been disabled, no need for RCU list traversal
* or kmemleak_lock held.
*/
- list_for_each_entry_safe(object, tmp, &object_list, object_list) {
+ list_for_each_entry_mutable(object, &object_list, object_list) {
__remove_object(object);
__delete_object(object);
diff --git a/mm/ksm.c b/mm/ksm.c
index 7d5b76478f0b..f42bc885f179 100644
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -1145,7 +1145,6 @@ static int remove_stable_node_chain(struct ksm_stable_node *stable_node,
struct rb_root *root)
{
struct ksm_stable_node *dup;
- struct hlist_node *hlist_safe;
if (!is_stable_node_chain(stable_node)) {
VM_BUG_ON(is_stable_node_dup(stable_node));
@@ -1155,8 +1154,7 @@ static int remove_stable_node_chain(struct ksm_stable_node *stable_node,
return false;
}
- hlist_for_each_entry_safe(dup, hlist_safe,
- &stable_node->hlist, hlist_dup) {
+ hlist_for_each_entry_mutable(dup, &stable_node->hlist, hlist_dup) {
VM_BUG_ON(!is_stable_node_dup(dup));
if (remove_stable_node(dup))
return true;
@@ -1168,7 +1166,7 @@ static int remove_stable_node_chain(struct ksm_stable_node *stable_node,
static int remove_all_stable_nodes(void)
{
- struct ksm_stable_node *stable_node, *next;
+ struct ksm_stable_node *stable_node;
int nid;
int err = 0;
@@ -1184,7 +1182,7 @@ static int remove_all_stable_nodes(void)
cond_resched();
}
}
- list_for_each_entry_safe(stable_node, next, &migrate_nodes, list) {
+ list_for_each_entry_mutable(stable_node, &migrate_nodes, list) {
if (remove_stable_node(stable_node))
err = -EBUSY;
cond_resched();
@@ -1665,7 +1663,6 @@ static struct folio *stable_node_dup(struct ksm_stable_node **_stable_node_dup,
bool prune_stale_stable_nodes)
{
struct ksm_stable_node *dup, *found = NULL, *stable_node = *_stable_node;
- struct hlist_node *hlist_safe;
struct folio *folio, *tree_folio = NULL;
int found_rmap_hlist_len;
@@ -1677,8 +1674,7 @@ static struct folio *stable_node_dup(struct ksm_stable_node **_stable_node_dup,
else
stable_node->chain_prune_time = jiffies;
- hlist_for_each_entry_safe(dup, hlist_safe,
- &stable_node->hlist, hlist_dup) {
+ hlist_for_each_entry_mutable(dup, &stable_node->hlist, hlist_dup) {
cond_resched();
/*
* We must walk all stable_node_dup to prune the stale
@@ -2611,11 +2607,10 @@ static struct ksm_rmap_item *scan_get_next_rmap_item(struct page **page)
* so prune them once before each full scan.
*/
if (!ksm_merge_across_nodes) {
- struct ksm_stable_node *stable_node, *next;
+ struct ksm_stable_node *stable_node;
struct folio *folio;
- list_for_each_entry_safe(stable_node, next,
- &migrate_nodes, list) {
+ list_for_each_entry_mutable(stable_node, &migrate_nodes, list) {
folio = ksm_get_folio(stable_node,
KSM_GET_FOLIO_NOLOCK);
if (folio)
@@ -3323,7 +3318,6 @@ static bool stable_node_chain_remove_range(struct ksm_stable_node *stable_node,
struct rb_root *root)
{
struct ksm_stable_node *dup;
- struct hlist_node *hlist_safe;
if (!is_stable_node_chain(stable_node)) {
VM_BUG_ON(is_stable_node_dup(stable_node));
@@ -3331,8 +3325,7 @@ static bool stable_node_chain_remove_range(struct ksm_stable_node *stable_node,
end_pfn);
}
- hlist_for_each_entry_safe(dup, hlist_safe,
- &stable_node->hlist, hlist_dup) {
+ hlist_for_each_entry_mutable(dup, &stable_node->hlist, hlist_dup) {
VM_BUG_ON(!is_stable_node_dup(dup));
stable_node_dup_remove_range(dup, start_pfn, end_pfn);
}
@@ -3346,7 +3339,7 @@ static bool stable_node_chain_remove_range(struct ksm_stable_node *stable_node,
static void ksm_check_stable_tree(unsigned long start_pfn,
unsigned long end_pfn)
{
- struct ksm_stable_node *stable_node, *next;
+ struct ksm_stable_node *stable_node;
struct rb_node *node;
int nid;
@@ -3364,7 +3357,7 @@ static void ksm_check_stable_tree(unsigned long start_pfn,
cond_resched();
}
}
- list_for_each_entry_safe(stable_node, next, &migrate_nodes, list) {
+ list_for_each_entry_mutable(stable_node, &migrate_nodes, list) {
if (stable_node->kpfn >= start_pfn &&
stable_node->kpfn < end_pfn)
remove_node_from_stable_tree(stable_node);
diff --git a/mm/list_lru.c b/mm/list_lru.c
index 36662d02ff96..ab9f48828a05 100644
--- a/mm/list_lru.c
+++ b/mm/list_lru.c
@@ -340,7 +340,7 @@ __list_lru_walk_one(struct list_lru *lru, int nid, struct mem_cgroup *memcg,
{
struct list_lru_node *nlru = &lru->node[nid];
struct list_lru_one *l = NULL;
- struct list_head *item, *n;
+ struct list_head *item;
unsigned long isolated = 0;
restart:
@@ -348,7 +348,7 @@ __list_lru_walk_one(struct list_lru *lru, int nid, struct mem_cgroup *memcg,
/*irq_flags=*/NULL, /*skip_empty=*/true);
if (!l)
return isolated;
- list_for_each_safe(item, n, &l->list) {
+ list_for_each_mutable(item, &l->list) {
enum lru_status ret;
/*
diff --git a/mm/memcontrol-v1.c b/mm/memcontrol-v1.c
index 765069211567..2e32f84a109a 100644
--- a/mm/memcontrol-v1.c
+++ b/mm/memcontrol-v1.c
@@ -986,11 +986,11 @@ static int mem_cgroup_oom_register_event(struct mem_cgroup *memcg,
static void mem_cgroup_oom_unregister_event(struct mem_cgroup *memcg,
struct eventfd_ctx *eventfd)
{
- struct mem_cgroup_eventfd_list *ev, *tmp;
+ struct mem_cgroup_eventfd_list *ev;
spin_lock(&memcg_oom_lock);
- list_for_each_entry_safe(ev, tmp, &memcg->oom_notify, list) {
+ list_for_each_entry_mutable(ev, &memcg->oom_notify, list) {
if (ev->eventfd == eventfd) {
list_del(&ev->list);
kfree(ev);
@@ -1242,7 +1242,7 @@ void memcg1_memcg_init(struct mem_cgroup *memcg)
void memcg1_css_offline(struct mem_cgroup *memcg)
{
- struct mem_cgroup_event *event, *tmp;
+ struct mem_cgroup_event *event;
/*
* Unregister events and notify userspace.
@@ -1250,7 +1250,7 @@ void memcg1_css_offline(struct mem_cgroup *memcg)
* directory to avoid race between userspace and kernelspace.
*/
spin_lock_irq(&memcg->event_list_lock);
- list_for_each_entry_safe(event, tmp, &memcg->event_list, list) {
+ list_for_each_entry_mutable(event, &memcg->event_list, list) {
list_del_init(&event->list);
schedule_work(&event->remove);
}
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 51508a55c405..e14d99adf378 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -423,9 +423,9 @@ static void add_to_kill_anon_file(struct task_struct *tsk, const struct page *p,
static bool task_in_to_kill_list(struct list_head *to_kill,
struct task_struct *tsk)
{
- struct to_kill *tk, *next;
+ struct to_kill *tk;
- list_for_each_entry_safe(tk, next, to_kill, nd) {
+ list_for_each_entry_mutable(tk, to_kill, nd) {
if (tk->tsk == tsk)
return true;
}
@@ -450,9 +450,9 @@ void add_to_kill_ksm(struct task_struct *tsk, const struct page *p,
static void kill_procs(struct list_head *to_kill, bool forcekill,
unsigned long pfn, int flags)
{
- struct to_kill *tk, *next;
+ struct to_kill *tk;
- list_for_each_entry_safe(tk, next, to_kill, nd) {
+ list_for_each_entry_mutable(tk, to_kill, nd) {
if (forcekill) {
if (tk->addr == -EFAULT) {
pr_err("%#lx: forcibly killing %s:%d because of failure to unmap corrupted page\n",
@@ -1860,11 +1860,11 @@ bool is_raw_hwpoison_page_in_hugepage(struct page *page)
static unsigned long __folio_free_raw_hwp(struct folio *folio, bool move_flag)
{
struct llist_node *head;
- struct raw_hwp_page *p, *next;
+ struct raw_hwp_page *p;
unsigned long count = 0;
head = llist_del_all(raw_hwp_list_head(folio));
- llist_for_each_entry_safe(p, next, head, node) {
+ llist_for_each_entry_mutable(p, head, node) {
if (move_flag)
SetPageHWPoison(p->page);
else
diff --git a/mm/memory-tiers.c b/mm/memory-tiers.c
index 54851d8a195b..4e0585925ae3 100644
--- a/mm/memory-tiers.c
+++ b/mm/memory-tiers.c
@@ -690,9 +690,9 @@ EXPORT_SYMBOL_GPL(mt_find_alloc_memory_type);
void mt_put_memory_types(struct list_head *memory_types)
{
- struct memory_dev_type *mtype, *mtn;
+ struct memory_dev_type *mtype;
- list_for_each_entry_safe(mtype, mtn, memory_types, list) {
+ list_for_each_entry_mutable(mtype, memory_types, list) {
list_del(&mtype->list);
put_memory_type(mtype);
}
diff --git a/mm/migrate.c b/mm/migrate.c
index d9b23909d716..acc7925d1d1b 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -257,9 +257,8 @@ static int migrate_movable_ops_page(struct page *dst, struct page *src,
void putback_movable_pages(struct list_head *l)
{
struct folio *folio;
- struct folio *folio2;
- list_for_each_entry_safe(folio, folio2, l, lru) {
+ list_for_each_entry_mutable(folio, l, lru) {
if (unlikely(folio_test_hugetlb(folio))) {
folio_putback_hugetlb(folio);
continue;
@@ -336,7 +335,7 @@ static bool try_to_map_unused_to_zeropage(struct page_vma_mapped_walk *pvmw,
}
struct rmap_walk_arg {
- struct folio *folio;
+ struct folio *folio, *folio2;
bool map_unused_to_zeropage;
};
@@ -1634,14 +1633,14 @@ static int migrate_hugetlbs(struct list_head *from, new_folio_t get_new_folio,
int nr_failed = 0;
int nr_retry_pages = 0;
int pass = 0;
- struct folio *folio, *folio2;
+ struct folio *folio;
int rc, nr_pages;
for (pass = 0; pass < NR_MAX_MIGRATE_PAGES_RETRY && retry; pass++) {
retry = 0;
nr_retry_pages = 0;
- list_for_each_entry_safe(folio, folio2, from, lru) {
+ list_for_each_entry_mutable(folio, from, lru) {
if (!folio_test_hugetlb(folio))
continue;
@@ -1722,14 +1721,14 @@ static void migrate_folios_move(struct list_head *src_folios,
int *retry, int *thp_retry, int *nr_failed,
int *nr_retry_pages)
{
- struct folio *folio, *folio2, *dst, *dst2;
+ struct folio *folio, *dst, *dst2;
bool is_thp;
int nr_pages;
int rc;
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) {
+ list_for_each_entry_mutable(folio, src_folios, lru) {
is_thp = folio_test_large(folio) && folio_test_pmd_mappable(folio);
nr_pages = folio_nr_pages(folio);
@@ -1770,11 +1769,11 @@ static void migrate_folios_undo(struct list_head *src_folios,
free_folio_t put_new_folio, unsigned long private,
struct list_head *ret_folios)
{
- struct folio *folio, *folio2, *dst, *dst2;
+ struct folio *folio, *dst, *dst2;
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) {
+ list_for_each_entry_mutable(folio, src_folios, lru) {
int old_folio_state = 0;
struct anon_vma *anon_vma = NULL;
@@ -1810,7 +1809,7 @@ static int migrate_pages_batch(struct list_head *from,
int pass = 0;
bool is_thp = false;
bool is_large = false;
- struct folio *folio, *folio2, *dst = NULL;
+ struct folio *folio, *dst = NULL;
int rc, rc_saved = 0, nr_pages;
LIST_HEAD(unmap_folios);
LIST_HEAD(dst_folios);
@@ -1824,7 +1823,7 @@ static int migrate_pages_batch(struct list_head *from,
thp_retry = 0;
nr_retry_pages = 0;
- list_for_each_entry_safe(folio, folio2, from, lru) {
+ list_for_each_entry_mutable(folio, from, lru) {
is_large = folio_test_large(folio);
is_thp = folio_test_pmd_mappable(folio);
nr_pages = folio_nr_pages(folio);
@@ -2109,7 +2108,7 @@ int migrate_pages(struct list_head *from, new_folio_t get_new_folio,
again:
nr_pages = 0;
- list_for_each_entry_safe(folio, folio2, from, lru) {
+ list_for_each_entry_mutable(folio, folio2, from, lru) {
/* Retried hugetlb folios will be kept in list */
if (folio_test_hugetlb(folio)) {
list_move_tail(&folio->lru, &ret_folios);
diff --git a/mm/mmu_notifier.c b/mm/mmu_notifier.c
index 245b74f39f91..7d4ccf9853a3 100644
--- a/mm/mmu_notifier.c
+++ b/mm/mmu_notifier.c
@@ -131,7 +131,6 @@ mn_itree_inv_next(struct mmu_interval_notifier *interval_sub,
static void mn_itree_inv_end(struct mmu_notifier_subscriptions *subscriptions)
{
struct mmu_interval_notifier *interval_sub;
- struct hlist_node *next;
spin_lock(&subscriptions->lock);
if (--subscriptions->active_invalidate_ranges ||
@@ -149,9 +148,7 @@ static void mn_itree_inv_end(struct mmu_notifier_subscriptions *subscriptions)
* they are progressed. This arrangement for tree updates is used to
* avoid using a blocking lock during invalidate_range_start.
*/
- hlist_for_each_entry_safe(interval_sub, next,
- &subscriptions->deferred_list,
- deferred_item) {
+ hlist_for_each_entry_mutable(interval_sub, &subscriptions->deferred_list, deferred_item) {
if (RB_EMPTY_NODE(&interval_sub->interval_tree.rb))
interval_tree_insert(&interval_sub->interval_tree,
&subscriptions->itree);
@@ -263,9 +260,9 @@ EXPORT_SYMBOL_GPL(mmu_interval_read_begin);
static void mn_itree_finish_pass(struct llist_head *finish_passes)
{
struct llist_node *first = llist_reverse_order(__llist_del_all(finish_passes));
- struct mmu_interval_notifier_finish *f, *next;
+ struct mmu_interval_notifier_finish *f;
- llist_for_each_entry_safe(f, next, first, link)
+ llist_for_each_entry_mutable(f, first, link)
f->notifier->ops->invalidate_finish(f);
}
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index ee902a468c2f..6d29df3e2973 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -1559,10 +1559,10 @@ static void free_one_page(struct zone *zone, struct page *page,
llhead = &zone->trylock_free_pages;
if (unlikely(!llist_empty(llhead) && !(fpi_flags & FPI_TRYLOCK))) {
struct llist_node *llnode;
- struct page *p, *tmp;
+ struct page *p;
llnode = llist_del_all(llhead);
- llist_for_each_entry_safe(p, tmp, llnode, pcp_llist) {
+ llist_for_each_entry_mutable(p, llnode, pcp_llist) {
unsigned int p_order = p->private;
split_large_buddy(zone, p, page_to_pfn(p), p_order, fpi_flags);
@@ -7022,10 +7022,10 @@ static void split_free_frozen_pages(struct list_head *list, gfp_t gfp_mask)
int order;
for (order = 0; order < NR_PAGE_ORDERS; order++) {
- struct page *page, *next;
+ struct page *page;
int nr_pages = 1 << order;
- list_for_each_entry_safe(page, next, &list[order], lru) {
+ list_for_each_entry_mutable(page, &list[order], lru) {
int i;
post_alloc_hook(page, order, gfp_mask);
diff --git a/mm/page_reporting.c b/mm/page_reporting.c
index 7418f2e500bb..849266216c9f 100644
--- a/mm/page_reporting.c
+++ b/mm/page_reporting.c
@@ -180,7 +180,7 @@ page_reporting_cycle(struct page_reporting_dev_info *prdev, struct zone *zone,
budget = DIV_ROUND_UP(area->nr_free, PAGE_REPORTING_CAPACITY * 16);
/* loop through free list adding unreported pages to sg list */
- list_for_each_entry_safe(page, next, list, lru) {
+ list_for_each_entry_mutable(page, next, list, lru) {
/* We are going to skip over the reported pages. */
if (PageReported(page))
continue;
diff --git a/mm/percpu.c b/mm/percpu.c
index b0676b8054ed..ae932e0e1ae6 100644
--- a/mm/percpu.c
+++ b/mm/percpu.c
@@ -1741,7 +1741,7 @@ void __percpu *pcpu_alloc_noprof(size_t size, size_t align, bool reserved,
bool do_warn;
struct obj_cgroup *objcg = NULL;
static atomic_t warn_limit = ATOMIC_INIT(10);
- struct pcpu_chunk *chunk, *next;
+ struct pcpu_chunk *chunk;
const char *err;
int slot, off, cpu, ret;
unsigned long flags;
@@ -1814,8 +1814,7 @@ void __percpu *pcpu_alloc_noprof(size_t size, size_t align, bool reserved,
restart:
/* search through normal chunks */
for (slot = pcpu_size_to_slot(size); slot <= pcpu_free_slot; slot++) {
- list_for_each_entry_safe(chunk, next, &pcpu_chunk_lists[slot],
- list) {
+ list_for_each_entry_mutable(chunk, &pcpu_chunk_lists[slot], list) {
off = pcpu_find_block_fit(chunk, bits, bit_align,
is_atomic);
if (off < 0) {
@@ -1952,7 +1951,7 @@ static void pcpu_balance_free(bool empty_only)
{
LIST_HEAD(to_free);
struct list_head *free_head = &pcpu_chunk_lists[pcpu_free_slot];
- struct pcpu_chunk *chunk, *next;
+ struct pcpu_chunk *chunk;
lockdep_assert_held(&pcpu_lock);
@@ -1960,7 +1959,7 @@ static void pcpu_balance_free(bool empty_only)
* There's no reason to keep around multiple unused chunks and VM
* areas can be scarce. Destroy all free chunks except for one.
*/
- list_for_each_entry_safe(chunk, next, free_head, list) {
+ list_for_each_entry_mutable(chunk, free_head, list) {
WARN_ON(chunk->immutable);
/* spare the first one */
@@ -1975,7 +1974,7 @@ static void pcpu_balance_free(bool empty_only)
return;
spin_unlock_irq(&pcpu_lock);
- list_for_each_entry_safe(chunk, next, &to_free, list) {
+ list_for_each_entry_mutable(chunk, &to_free, list) {
unsigned int rs, re;
for_each_set_bitrange(rs, re, chunk->populated, chunk->nr_pages) {
diff --git a/mm/pgtable-generic.c b/mm/pgtable-generic.c
index b91b1a98029c..723b4bdb447d 100644
--- a/mm/pgtable-generic.c
+++ b/mm/pgtable-generic.c
@@ -426,7 +426,7 @@ static struct {
static void kernel_pgtable_work_func(struct work_struct *work)
{
- struct ptdesc *pt, *next;
+ struct ptdesc *pt;
LIST_HEAD(page_list);
spin_lock(&kernel_pgtable_work.lock);
@@ -434,7 +434,7 @@ static void kernel_pgtable_work_func(struct work_struct *work)
spin_unlock(&kernel_pgtable_work.lock);
iommu_sva_invalidate_kva_range(PAGE_OFFSET, TLB_FLUSH_ALL);
- list_for_each_entry_safe(pt, next, &page_list, pt_list)
+ list_for_each_entry_mutable(pt, &page_list, pt_list)
__pagetable_free(pt);
}
diff --git a/mm/rmap.c b/mm/rmap.c
index 1c77d5dc06e9..37164f446d2d 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -451,9 +451,9 @@ int anon_vma_fork(struct vm_area_struct *vma, struct vm_area_struct *pvma)
*/
static void cleanup_partial_anon_vmas(struct vm_area_struct *vma)
{
- struct anon_vma_chain *avc, *next;
+ struct anon_vma_chain *avc;
- list_for_each_entry_safe(avc, next, &vma->anon_vma_chain, same_vma) {
+ list_for_each_entry_mutable(avc, &vma->anon_vma_chain, same_vma) {
list_del(&avc->same_vma);
anon_vma_chain_free(avc);
}
@@ -478,7 +478,7 @@ static void cleanup_partial_anon_vmas(struct vm_area_struct *vma)
*/
void unlink_anon_vmas(struct vm_area_struct *vma)
{
- struct anon_vma_chain *avc, *next;
+ struct anon_vma_chain *avc;
struct anon_vma *active_anon_vma = vma->anon_vma;
/* Always hold mmap lock, read-lock on unmap possibly. */
@@ -496,7 +496,7 @@ void unlink_anon_vmas(struct vm_area_struct *vma)
* Unlink each anon_vma chained to the VMA. This list is ordered
* from newest to oldest, ensuring the root anon_vma gets freed last.
*/
- list_for_each_entry_safe(avc, next, &vma->anon_vma_chain, same_vma) {
+ list_for_each_entry_mutable(avc, &vma->anon_vma_chain, same_vma) {
struct anon_vma *anon_vma = avc->anon_vma;
anon_vma_interval_tree_remove(avc, &anon_vma->rb_root);
@@ -528,7 +528,7 @@ void unlink_anon_vmas(struct vm_area_struct *vma)
* anon_vmas, destroy them. Could not do before due to __put_anon_vma()
* needing to write-acquire the anon_vma->root->rwsem.
*/
- list_for_each_entry_safe(avc, next, &vma->anon_vma_chain, same_vma) {
+ list_for_each_entry_mutable(avc, &vma->anon_vma_chain, same_vma) {
struct anon_vma *anon_vma = avc->anon_vma;
VM_WARN_ON(anon_vma->num_children);
diff --git a/mm/shmem.c b/mm/shmem.c
index b51f83c970bb..9f03e46bfde2 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -727,7 +727,8 @@ static const char *shmem_format_huge(int huge)
static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo,
struct shrink_control *sc, unsigned long nr_to_free)
{
- LIST_HEAD(list), *pos, *next;
+ LIST_HEAD(list);
+ struct list_head *pos;
struct inode *inode;
struct shmem_inode_info *info;
struct folio *folio;
@@ -738,7 +739,7 @@ static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo,
return SHRINK_STOP;
spin_lock(&sbinfo->shrinklist_lock);
- list_for_each_safe(pos, next, &sbinfo->shrinklist) {
+ list_for_each_mutable(pos, &sbinfo->shrinklist) {
info = list_entry(pos, struct shmem_inode_info, shrinklist);
/* pin the inode */
@@ -758,7 +759,7 @@ static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo,
}
spin_unlock(&sbinfo->shrinklist_lock);
- list_for_each_safe(pos, next, &list) {
+ list_for_each_mutable(pos, &list) {
pgoff_t next, end;
loff_t i_size;
int ret;
@@ -1547,7 +1548,7 @@ int shmem_unuse(unsigned int type)
spin_lock(&shmem_swaplist_lock);
start_over:
- list_for_each_entry_safe(info, next, &shmem_swaplist, swaplist) {
+ list_for_each_entry_mutable(info, next, &shmem_swaplist, swaplist) {
if (!info->swapped) {
list_del_init(&info->swaplist);
continue;
diff --git a/mm/slab_common.c b/mm/slab_common.c
index b6426d7ceec9..489e8e0800b6 100644
--- a/mm/slab_common.c
+++ b/mm/slab_common.c
@@ -1465,7 +1465,7 @@ static int
drain_page_cache(struct kfree_rcu_cpu *krcp)
{
unsigned long flags;
- struct llist_node *page_list, *pos, *n;
+ struct llist_node *page_list, *pos;
int freed = 0;
if (!rcu_min_cached_objs)
@@ -1476,7 +1476,7 @@ drain_page_cache(struct kfree_rcu_cpu *krcp)
WRITE_ONCE(krcp->nr_bkv_objs, 0);
raw_spin_unlock_irqrestore(&krcp->lock, flags);
- llist_for_each_safe(pos, n, page_list) {
+ llist_for_each_mutable(pos, page_list) {
free_page((unsigned long)pos);
freed++;
}
@@ -1550,7 +1550,7 @@ kvfree_rcu_list(struct rcu_head *head)
static void kfree_rcu_work(struct work_struct *work)
{
unsigned long flags;
- struct kvfree_rcu_bulk_data *bnode, *n;
+ struct kvfree_rcu_bulk_data *bnode;
struct list_head bulk_head[FREE_N_CHANNELS];
struct rcu_head *head;
struct kfree_rcu_cpu *krcp;
@@ -1576,7 +1576,7 @@ static void kfree_rcu_work(struct work_struct *work)
// Handle the first two channels.
for (i = 0; i < FREE_N_CHANNELS; i++) {
// Start from the tail page, so a GP is likely passed for it.
- list_for_each_entry_safe(bnode, n, &bulk_head[i], list)
+ list_for_each_entry_mutable(bnode, &bulk_head[i], list)
kvfree_rcu_bulk(krcp, bnode, i);
}
@@ -1674,7 +1674,7 @@ static void
kvfree_rcu_drain_ready(struct kfree_rcu_cpu *krcp)
{
struct list_head bulk_ready[FREE_N_CHANNELS];
- struct kvfree_rcu_bulk_data *bnode, *n;
+ struct kvfree_rcu_bulk_data *bnode;
struct rcu_head *head_ready = NULL;
unsigned long flags;
int i;
@@ -1683,7 +1683,7 @@ kvfree_rcu_drain_ready(struct kfree_rcu_cpu *krcp)
for (i = 0; i < FREE_N_CHANNELS; i++) {
INIT_LIST_HEAD(&bulk_ready[i]);
- list_for_each_entry_safe_reverse(bnode, n, &krcp->bulk_head[i], list) {
+ list_for_each_entry_mutable_reverse(bnode, &krcp->bulk_head[i], list) {
if (!poll_state_synchronize_rcu_full(&bnode->gp_snap))
break;
@@ -1700,7 +1700,7 @@ kvfree_rcu_drain_ready(struct kfree_rcu_cpu *krcp)
raw_spin_unlock_irqrestore(&krcp->lock, flags);
for (i = 0; i < FREE_N_CHANNELS; i++) {
- list_for_each_entry_safe(bnode, n, &bulk_ready[i], list)
+ list_for_each_entry_mutable(bnode, &bulk_ready[i], list)
kvfree_rcu_bulk(krcp, bnode, i);
}
diff --git a/mm/slub.c b/mm/slub.c
index 9ec774dc7009..6f4a79e32d75 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -3253,7 +3253,7 @@ static void barn_shrink(struct kmem_cache *s, struct node_barn *barn)
{
LIST_HEAD(empty_list);
LIST_HEAD(full_list);
- struct slab_sheaf *sheaf, *sheaf2;
+ struct slab_sheaf *sheaf;
unsigned long flags;
spin_lock_irqsave(&barn->lock, flags);
@@ -3265,12 +3265,12 @@ static void barn_shrink(struct kmem_cache *s, struct node_barn *barn)
spin_unlock_irqrestore(&barn->lock, flags);
- list_for_each_entry_safe(sheaf, sheaf2, &full_list, barn_list) {
+ list_for_each_entry_mutable(sheaf, &full_list, barn_list) {
sheaf_flush_unused(s, sheaf);
free_empty_sheaf(s, sheaf);
}
- list_for_each_entry_safe(sheaf, sheaf2, &empty_list, barn_list)
+ list_for_each_entry_mutable(sheaf, &empty_list, barn_list)
free_empty_sheaf(s, sheaf);
}
@@ -3757,7 +3757,7 @@ static bool get_partial_node_bulk(struct kmem_cache *s,
struct partial_bulk_context *pc,
bool allow_spin)
{
- struct slab *slab, *slab2;
+ struct slab *slab;
struct slab *first = NULL, *last = NULL;
unsigned int total_free = 0;
unsigned long flags;
@@ -3773,7 +3773,7 @@ static bool get_partial_node_bulk(struct kmem_cache *s,
else if (!spin_trylock_irqsave(&n->list_lock, flags))
return false;
- list_for_each_entry_safe(slab, slab2, &n->partial, slab_list) {
+ list_for_each_entry_mutable(slab, &n->partial, slab_list) {
struct freelist_counters flc;
unsigned int slab_free;
@@ -3828,7 +3828,7 @@ static void *get_from_partial_node(struct kmem_cache *s,
gfp_t gfp_flags,
const struct slab_alloc_context *ac)
{
- struct slab *slab, *slab2;
+ struct slab *slab;
unsigned long flags;
void *object = NULL;
@@ -3845,7 +3845,7 @@ static void *get_from_partial_node(struct kmem_cache *s,
spin_lock_irqsave(&n->list_lock, flags);
else if (!spin_trylock_irqsave(&n->list_lock, flags))
return NULL;
- list_for_each_entry_safe(slab, slab2, &n->partial, slab_list) {
+ list_for_each_entry_mutable(slab, &n->partial, slab_list) {
struct freelist_counters old, new;
@@ -6345,13 +6345,13 @@ static void free_deferred_objects(struct irq_work *work)
{
struct defer_free *df = container_of(work, struct defer_free, work);
struct llist_head *objs = &df->objects;
- struct llist_node *llnode, *pos, *t;
+ struct llist_node *llnode, *pos;
if (llist_empty(objs))
return;
llnode = llist_del_all(objs);
- llist_for_each_safe(pos, t, llnode) {
+ llist_for_each_mutable(pos, llnode) {
struct kmem_cache *s;
struct slab *slab;
void *x = pos;
@@ -7185,7 +7185,7 @@ __refill_objects_node(struct kmem_cache *s, void **p, gfp_t gfp, unsigned int mi
bool allow_spin)
{
struct partial_bulk_context pc;
- struct slab *slab, *slab2;
+ struct slab *slab;
unsigned int refilled = 0;
unsigned long flags;
void *object;
@@ -7197,7 +7197,7 @@ __refill_objects_node(struct kmem_cache *s, void **p, gfp_t gfp, unsigned int mi
if (!get_partial_node_bulk(s, n, &pc, allow_spin))
return 0;
- list_for_each_entry_safe(slab, slab2, &pc.slabs, slab_list) {
+ list_for_each_entry_mutable(slab, &pc.slabs, slab_list) {
unsigned int count;
@@ -8031,11 +8031,11 @@ static void list_slab_objects(struct kmem_cache *s, struct slab *slab)
static void free_partial(struct kmem_cache *s, struct kmem_cache_node *n)
{
LIST_HEAD(discard);
- struct slab *slab, *h;
+ struct slab *slab;
BUG_ON(irqs_disabled());
spin_lock_irq(&n->list_lock);
- list_for_each_entry_safe(slab, h, &n->partial, slab_list) {
+ list_for_each_entry_mutable(slab, &n->partial, slab_list) {
if (!slab->inuse) {
remove_partial(n, slab);
list_add(&slab->slab_list, &discard);
@@ -8045,7 +8045,7 @@ static void free_partial(struct kmem_cache *s, struct kmem_cache_node *n)
}
spin_unlock_irq(&n->list_lock);
- list_for_each_entry_safe(slab, h, &discard, slab_list)
+ list_for_each_entry_mutable(slab, &discard, slab_list)
discard_slab(s, slab);
}
@@ -8286,7 +8286,6 @@ static int __kmem_cache_do_shrink(struct kmem_cache *s)
int i;
struct kmem_cache_node *n;
struct slab *slab;
- struct slab *t;
struct list_head discard;
struct list_head promote[SHRINK_PROMOTE_MAX];
unsigned long flags;
@@ -8312,7 +8311,7 @@ static int __kmem_cache_do_shrink(struct kmem_cache *s)
* Note that concurrent frees may occur while we hold the
* list_lock. slab->inuse here is the upper limit.
*/
- list_for_each_entry_safe(slab, t, &n->partial, slab_list) {
+ list_for_each_entry_mutable(slab, &n->partial, slab_list) {
int free = slab->objects - slab->inuse;
/* Do not reread slab->inuse */
@@ -8339,7 +8338,7 @@ static int __kmem_cache_do_shrink(struct kmem_cache *s)
spin_unlock_irqrestore(&n->list_lock, flags);
/* Release empty slabs */
- list_for_each_entry_safe(slab, t, &discard, slab_list)
+ list_for_each_entry_mutable(slab, &discard, slab_list)
free_slab(s, slab);
if (node_nr_slabs(n))
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 78b49b0658ad..e050b3894d6f 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -2825,14 +2825,14 @@ static int try_to_unuse(unsigned int type)
*/
static void drain_mmlist(void)
{
- struct list_head *p, *next;
+ struct list_head *p;
unsigned int type;
for (type = 0; type < nr_swapfiles; type++)
if (swap_usage_in_pages(swap_info[type]))
return;
spin_lock(&mmlist_lock);
- list_for_each_safe(p, next, &init_mm.mmlist)
+ list_for_each_mutable(p, &init_mm.mmlist)
list_del_init(p);
spin_unlock(&mmlist_lock);
}
diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c
index b8d2d87ce8d7..78ef5f7e3f67 100644
--- a/mm/userfaultfd.c
+++ b/mm/userfaultfd.c
@@ -3010,9 +3010,9 @@ static void dup_fctx(struct userfaultfd_fork_ctx *fctx)
void dup_userfaultfd_complete(struct list_head *fcs)
{
- struct userfaultfd_fork_ctx *fctx, *n;
+ struct userfaultfd_fork_ctx *fctx;
- list_for_each_entry_safe(fctx, n, fcs, list) {
+ list_for_each_entry_mutable(fctx, fcs, list) {
dup_fctx(fctx);
list_del(&fctx->list);
kfree(fctx);
@@ -3021,7 +3021,7 @@ void dup_userfaultfd_complete(struct list_head *fcs)
void dup_userfaultfd_fail(struct list_head *fcs)
{
- struct userfaultfd_fork_ctx *fctx, *n;
+ struct userfaultfd_fork_ctx *fctx;
/*
* An error has occurred on fork, we will tear memory down, but have
@@ -3033,7 +3033,7 @@ void dup_userfaultfd_fail(struct list_head *fcs)
*
* mm tear down will take care of cleaning up VMA contexts.
*/
- list_for_each_entry_safe(fctx, n, fcs, list) {
+ list_for_each_entry_mutable(fctx, fcs, list) {
struct userfaultfd_ctx *octx = fctx->orig;
struct userfaultfd_ctx *ctx = fctx->new;
@@ -3170,10 +3170,10 @@ int userfaultfd_unmap_prep(struct vm_area_struct *vma, unsigned long start,
void userfaultfd_unmap_complete(struct mm_struct *mm, struct list_head *uf)
{
- struct userfaultfd_unmap_ctx *ctx, *n;
+ struct userfaultfd_unmap_ctx *ctx;
struct userfaultfd_wait_queue ewq;
- list_for_each_entry_safe(ctx, n, uf, list) {
+ list_for_each_entry_mutable(ctx, uf, list) {
msg_init(&ewq.msg);
ewq.msg.event = UFFD_EVENT_UNMAP;
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index 1afca3568b9b..2b510e7651df 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -2202,13 +2202,13 @@ static void purge_fragmented_blocks_allcpus(void);
static void
reclaim_list_global(struct list_head *head)
{
- struct vmap_area *va, *n;
+ struct vmap_area *va;
if (list_empty(head))
return;
spin_lock(&free_vmap_area_lock);
- list_for_each_entry_safe(va, n, head, list)
+ list_for_each_entry_mutable(va, head, list)
merge_or_add_vmap_area_augment(va,
&free_vmap_area_root, &free_vmap_area_list);
spin_unlock(&free_vmap_area_lock);
@@ -2219,7 +2219,7 @@ decay_va_pool_node(struct vmap_node *vn, bool full_decay)
{
LIST_HEAD(decay_list);
struct rb_root decay_root = RB_ROOT;
- struct vmap_area *va, *nva;
+ struct vmap_area *va;
unsigned long n_decay, pool_len;
int i;
@@ -2242,7 +2242,7 @@ decay_va_pool_node(struct vmap_node *vn, bool full_decay)
n_decay >>= 2;
pool_len -= n_decay;
- list_for_each_entry_safe(va, nva, &tmp_list, list) {
+ list_for_each_entry_mutable(va, &tmp_list, list) {
if (!n_decay--)
break;
@@ -2299,7 +2299,7 @@ static void purge_vmap_node(struct work_struct *work)
struct vmap_node *vn = container_of(work,
struct vmap_node, purge_work);
unsigned long nr_purged_pages = 0;
- struct vmap_area *va, *n_va;
+ struct vmap_area *va;
LIST_HEAD(local_list);
if (IS_ENABLED(CONFIG_KASAN_VMALLOC))
@@ -2307,7 +2307,7 @@ static void purge_vmap_node(struct work_struct *work)
vn->nr_purged = 0;
- list_for_each_entry_safe(va, n_va, &vn->purge_list, list) {
+ list_for_each_entry_mutable(va, &vn->purge_list, list) {
unsigned long nr = va_size(va) >> PAGE_SHIFT;
unsigned int vn_id = decode_vn_id(va->flags);
@@ -2803,9 +2803,9 @@ static bool purge_fragmented_block(struct vmap_block *vb,
static void free_purged_blocks(struct list_head *purge_list)
{
- struct vmap_block *vb, *n_vb;
+ struct vmap_block *vb;
- list_for_each_entry_safe(vb, n_vb, purge_list, purge) {
+ list_for_each_entry_mutable(vb, purge_list, purge) {
list_del(&vb->purge);
free_vmap_block(vb);
}
@@ -3386,9 +3386,9 @@ static void vm_reset_perms(struct vm_struct *area)
static void delayed_vfree_work(struct work_struct *w)
{
struct vfree_deferred *p = container_of(w, struct vfree_deferred, wq);
- struct llist_node *t, *llnode;
+ struct llist_node *llnode;
- llist_for_each_safe(llnode, t, llist_del_all(&p->list))
+ llist_for_each_mutable(llnode, llist_del_all(&p->list))
vfree(llnode);
}
@@ -3775,14 +3775,14 @@ vm_area_alloc_pages(gfp_t gfp, int nid,
static LLIST_HEAD(pending_vm_area_cleanup);
static void cleanup_vm_area_work(struct work_struct *work)
{
- struct vm_struct *area, *tmp;
+ struct vm_struct *area;
struct llist_node *head;
head = llist_del_all(&pending_vm_area_cleanup);
if (!head)
return;
- llist_for_each_entry_safe(area, tmp, head, llnode) {
+ llist_for_each_entry_mutable(area, head, llnode) {
if (!area->pages)
free_vm_area(area);
else
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 35c3bb15ae96..d7c4ded7a8fe 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -1598,11 +1598,11 @@ unsigned int reclaim_clean_pages_from_list(struct zone *zone,
};
struct reclaim_stat stat;
unsigned int nr_reclaimed;
- struct folio *folio, *next;
+ struct folio *folio;
LIST_HEAD(clean_folios);
unsigned int noreclaim_flag;
- list_for_each_entry_safe(folio, next, folio_list, lru) {
+ list_for_each_entry_mutable(folio, folio_list, lru) {
/* TODO: these pages should not even appear in this list. */
if (page_has_movable_ops(&folio->page))
continue;
@@ -4805,7 +4805,6 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
LIST_HEAD(list);
LIST_HEAD(clean);
struct folio *folio;
- struct folio *next;
enum node_stat_item item;
struct reclaim_stat stat;
struct lru_gen_mm_walk *walk;
@@ -4841,7 +4840,7 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
type_scanned, reclaimed, &stat, sc->priority,
type ? LRU_INACTIVE_FILE : LRU_INACTIVE_ANON);
- list_for_each_entry_safe_reverse(folio, next, &list, lru) {
+ list_for_each_entry_mutable_reverse(folio, &list, lru) {
DEFINE_MIN_SEQ(lruvec);
if (!folio_evictable(folio)) {
diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
index 83f5820c45f9..2ac86c758e0b 100644
--- a/mm/zsmalloc.c
+++ b/mm/zsmalloc.c
@@ -1806,7 +1806,7 @@ static void async_free_zspage(struct work_struct *work)
{
int i;
struct size_class *class;
- struct zspage *zspage, *tmp;
+ struct zspage *zspage;
LIST_HEAD(free_pages);
struct zs_pool *pool = container_of(work, struct zs_pool,
free_work);
@@ -1822,7 +1822,7 @@ static void async_free_zspage(struct work_struct *work)
spin_unlock(&class->lock);
}
- list_for_each_entry_safe(zspage, tmp, &free_pages, list) {
+ list_for_each_entry_mutable(zspage, &free_pages, list) {
list_del(&zspage->list);
lock_zspage(zspage);
--
2.43.0
^ permalink raw reply related
* [PATCH v3 4/7] block: Use mutable list iterators
From: Kaitao Cheng @ 2026-06-22 4:19 UTC (permalink / raw)
To: Yu Kuai, Jens Axboe, Tejun Heo, Josef Bacik,
Richard Russon (FlatCap), Jonathan Derrick
Cc: linux-block, linux-kernel, cgroups, linux-ntfs-dev, Kaitao Cheng
From: Kaitao Cheng <chengkaitao@kylinos.cn>
The safe list iterators require callers to provide a temporary cursor
even when the cursor is only used by the iterator itself. The mutable
iterator variants keep the same removal-safe traversal semantics while
allowing those internal cursors to be hidden from the call sites.
Convert block users of list, hlist and llist safe iterators to the new
mutable helpers. Drop the now-unused temporary cursor variables where
the loop body does not inspect or reset them.
This is a mechanical cleanup with no intended change in traversal order
or list mutation behavior.
Signed-off-by: Kaitao Cheng <chengkaitao@kylinos.cn>
---
block/bfq-iosched.c | 17 +++++++----------
block/blk-cgroup.c | 12 ++++++------
block/blk-flush.c | 4 ++--
block/blk-iocost.c | 18 +++++++++---------
block/blk-mq.c | 8 ++++----
block/blk-throttle.c | 4 ++--
block/kyber-iosched.c | 4 ++--
block/partitions/ldm.c | 8 ++++----
block/sed-opal.c | 4 ++--
9 files changed, 38 insertions(+), 41 deletions(-)
diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c
index 141c602d5e85..78e32ba0553d 100644
--- a/block/bfq-iosched.c
+++ b/block/bfq-iosched.c
@@ -1209,9 +1209,8 @@ static int bfqq_process_refs(struct bfq_queue *bfqq)
static void bfq_reset_burst_list(struct bfq_data *bfqd, struct bfq_queue *bfqq)
{
struct bfq_queue *item;
- struct hlist_node *n;
- hlist_for_each_entry_safe(item, n, &bfqd->burst_list, burst_list_node)
+ hlist_for_each_entry_mutable(item, &bfqd->burst_list, burst_list_node)
hlist_del_init(&item->burst_list_node);
/*
@@ -1236,7 +1235,6 @@ static void bfq_add_to_burst(struct bfq_data *bfqd, struct bfq_queue *bfqq)
if (bfqd->burst_size == bfqd->bfq_large_burst_thresh) {
struct bfq_queue *pos, *bfqq_item;
- struct hlist_node *n;
/*
* Enough queues have been activated shortly after each
@@ -1260,8 +1258,8 @@ static void bfq_add_to_burst(struct bfq_data *bfqd, struct bfq_queue *bfqq)
* belonging to a large burst. So the burst list is not
* needed any more. Remove it.
*/
- hlist_for_each_entry_safe(pos, n, &bfqd->burst_list,
- burst_list_node)
+ hlist_for_each_entry_mutable(pos, &bfqd->burst_list,
+ burst_list_node)
hlist_del_init(&pos->burst_list_node);
} else /*
* Burst not yet large: add bfqq to the burst list. Do
@@ -5330,7 +5328,6 @@ static struct request *bfq_dispatch_request(struct blk_mq_hw_ctx *hctx)
void bfq_put_queue(struct bfq_queue *bfqq)
{
struct bfq_queue *item;
- struct hlist_node *n;
struct bfq_group *bfqg = bfqq_group(bfqq);
bfq_log_bfqq(bfqq->bfqd, bfqq, "put_queue: %p %d", bfqq, bfqq->ref);
@@ -5391,8 +5388,8 @@ void bfq_put_queue(struct bfq_queue *bfqq)
hlist_del_init(&bfqq->woken_list_node);
/* reset waker for all queues in woken list */
- hlist_for_each_entry_safe(item, n, &bfqq->woken_list,
- woken_list_node) {
+ hlist_for_each_entry_mutable(item, &bfqq->woken_list,
+ woken_list_node) {
item->waker_bfqq = NULL;
hlist_del_init(&item->woken_list_node);
}
@@ -7141,13 +7138,13 @@ static void bfq_depth_updated(struct request_queue *q)
static void bfq_exit_queue(struct elevator_queue *e)
{
struct bfq_data *bfqd = e->elevator_data;
- struct bfq_queue *bfqq, *n;
+ struct bfq_queue *bfqq;
unsigned int actuator;
hrtimer_cancel(&bfqd->idle_slice_timer);
spin_lock_irq(&bfqd->lock);
- list_for_each_entry_safe(bfqq, n, &bfqd->idle_list, bfqq_list)
+ list_for_each_entry_mutable(bfqq, &bfqd->idle_list, bfqq_list)
bfq_deactivate_bfqq(bfqd, bfqq, false, false);
spin_unlock_irq(&bfqd->lock);
diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c
index 3093c1c03902..ced900019f2e 100644
--- a/block/blk-cgroup.c
+++ b/block/blk-cgroup.c
@@ -997,7 +997,7 @@ static void __blkcg_rstat_flush(struct blkcg *blkcg, int cpu)
{
struct llist_head *lhead = per_cpu_ptr(blkcg->lhead, cpu);
struct llist_node *lnode;
- struct blkg_iostat_set *bisc, *next_bisc;
+ struct blkg_iostat_set *bisc;
unsigned long flags;
rcu_read_lock();
@@ -1017,17 +1017,17 @@ static void __blkcg_rstat_flush(struct blkcg *blkcg, int cpu)
/*
* Iterate only the iostat_cpu's queued in the lockless list.
*/
- llist_for_each_entry_safe(bisc, next_bisc, lnode, lnode) {
+ llist_for_each_entry_mutable(bisc, lnode, lnode) {
struct blkcg_gq *blkg = bisc->blkg;
struct blkcg_gq *parent = blkg->parent;
struct blkg_iostat cur;
unsigned int seq;
/*
- * Order assignment of `next_bisc` from `bisc->lnode.next` in
- * llist_for_each_entry_safe and clearing `bisc->lqueued` for
- * avoiding to assign `next_bisc` with new next pointer added
- * in blk_cgroup_bio_start() in case of re-ordering.
+ * Order the iterator's internal `bisc->lnode.next` load before
+ * clearing `bisc->lqueued`, so the iterator can't pick up a new
+ * next pointer added in blk_cgroup_bio_start() in case of
+ * re-ordering.
*
* The pair barrier is implied in llist_add() in blk_cgroup_bio_start().
*/
diff --git a/block/blk-flush.c b/block/blk-flush.c
index 403a46c86411..20654c2103f2 100644
--- a/block/blk-flush.c
+++ b/block/blk-flush.c
@@ -204,7 +204,7 @@ static enum rq_end_io_ret flush_end_io(struct request *flush_rq,
{
struct request_queue *q = flush_rq->q;
struct list_head *running;
- struct request *rq, *n;
+ struct request *rq;
unsigned long flags = 0;
struct blk_flush_queue *fq = blk_get_flush_queue(flush_rq->mq_ctx);
@@ -243,7 +243,7 @@ static enum rq_end_io_ret flush_end_io(struct request *flush_rq,
fq->flush_running_idx ^= 1;
/* and push the waiting requests to the next stage */
- list_for_each_entry_safe(rq, n, running, queuelist) {
+ list_for_each_entry_mutable(rq, running, queuelist) {
unsigned int seq = blk_flush_cur_seq(rq);
BUG_ON(seq != REQ_FSEQ_PREFLUSH && seq != REQ_FSEQ_POSTFLUSH);
diff --git a/block/blk-iocost.c b/block/blk-iocost.c
index 563cc7dcf348..2ca18e52bc13 100644
--- a/block/blk-iocost.c
+++ b/block/blk-iocost.c
@@ -1718,7 +1718,7 @@ static void iocg_flush_stat_leaf(struct ioc_gq *iocg, struct ioc_now *now)
static void iocg_flush_stat(struct list_head *target_iocgs, struct ioc_now *now)
{
LIST_HEAD(inner_walk);
- struct ioc_gq *iocg, *tiocg;
+ struct ioc_gq *iocg;
/* flush leaves and build inner node walk list */
list_for_each_entry(iocg, target_iocgs, active_list) {
@@ -1727,7 +1727,7 @@ static void iocg_flush_stat(struct list_head *target_iocgs, struct ioc_now *now)
}
/* keep flushing upwards by walking the inner list backwards */
- list_for_each_entry_safe_reverse(iocg, tiocg, &inner_walk, walk_list) {
+ list_for_each_entry_mutable_reverse(iocg, &inner_walk, walk_list) {
iocg_flush_stat_upward(iocg);
list_del_init(&iocg->walk_list);
}
@@ -1848,7 +1848,7 @@ static void transfer_surpluses(struct list_head *surpluses, struct ioc_now *now)
{
LIST_HEAD(over_hwa);
LIST_HEAD(inner_walk);
- struct ioc_gq *iocg, *tiocg, *root_iocg;
+ struct ioc_gq *iocg, *root_iocg;
u32 after_sum, over_sum, over_target, gamma;
/*
@@ -1884,7 +1884,7 @@ static void transfer_surpluses(struct list_head *surpluses, struct ioc_now *now)
over_target = 0;
}
- list_for_each_entry_safe(iocg, tiocg, &over_hwa, walk_list) {
+ list_for_each_entry_mutable(iocg, &over_hwa, walk_list) {
if (over_target)
iocg->hweight_after_donation =
div_u64((u64)iocg->hweight_after_donation *
@@ -2055,7 +2055,7 @@ static void transfer_surpluses(struct list_head *surpluses, struct ioc_now *now)
}
/* walk list should be dissolved after use */
- list_for_each_entry_safe(iocg, tiocg, &inner_walk, walk_list)
+ list_for_each_entry_mutable(iocg, &inner_walk, walk_list)
list_del_init(&iocg->walk_list);
}
@@ -2166,9 +2166,9 @@ static void ioc_forgive_debts(struct ioc *ioc, u64 usage_us_sum, int nr_debtors,
static int ioc_check_iocgs(struct ioc *ioc, struct ioc_now *now)
{
int nr_debtors = 0;
- struct ioc_gq *iocg, *tiocg;
+ struct ioc_gq *iocg;
- list_for_each_entry_safe(iocg, tiocg, &ioc->active_iocgs, active_list) {
+ list_for_each_entry_mutable(iocg, &ioc->active_iocgs, active_list) {
if (!waitqueue_active(&iocg->waitq) && !iocg->abs_vdebt &&
!iocg->delay && !iocg_is_idle(iocg))
continue;
@@ -2234,7 +2234,7 @@ static int ioc_check_iocgs(struct ioc *ioc, struct ioc_now *now)
static void ioc_timer_fn(struct timer_list *timer)
{
struct ioc *ioc = container_of(timer, struct ioc, timer);
- struct ioc_gq *iocg, *tiocg;
+ struct ioc_gq *iocg;
struct ioc_now now;
LIST_HEAD(surpluses);
int nr_debtors, nr_shortages = 0, nr_lagging = 0;
@@ -2378,7 +2378,7 @@ static void ioc_timer_fn(struct timer_list *timer)
commit_weights(ioc);
/* surplus list should be dissolved after use */
- list_for_each_entry_safe(iocg, tiocg, &surpluses, surplus_list)
+ list_for_each_entry_mutable(iocg, &surpluses, surplus_list)
list_del_init(&iocg->surplus_list);
/*
diff --git a/block/blk-mq.c b/block/blk-mq.c
index 88cb5acc4f39..2daed45ad4e7 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -1216,9 +1216,9 @@ EXPORT_SYMBOL_GPL(blk_mq_end_request_batch);
static void blk_complete_reqs(struct llist_head *list)
{
struct llist_node *entry = llist_reverse_order(llist_del_all(list));
- struct request *rq, *next;
+ struct request *rq;
- llist_for_each_entry_safe(rq, next, entry, ipi_list)
+ llist_for_each_entry_mutable(rq, entry, ipi_list)
rq->q->mq_ops->complete(rq);
}
@@ -4383,14 +4383,14 @@ static int blk_mq_alloc_ctxs(struct request_queue *q)
*/
void blk_mq_release(struct request_queue *q)
{
- struct blk_mq_hw_ctx *hctx, *next;
+ struct blk_mq_hw_ctx *hctx;
unsigned long i;
queue_for_each_hw_ctx(q, hctx, i)
WARN_ON_ONCE(hctx && list_empty(&hctx->hctx_list));
/* all hctx are in .unused_hctx_list now */
- list_for_each_entry_safe(hctx, next, &q->unused_hctx_list, hctx_list) {
+ list_for_each_entry_mutable(hctx, &q->unused_hctx_list, hctx_list) {
list_del_init(&hctx->hctx_list);
kobject_put(&hctx->kobj);
}
diff --git a/block/blk-throttle.c b/block/blk-throttle.c
index 47052ba21d1b..1dd4901f00f3 100644
--- a/block/blk-throttle.c
+++ b/block/blk-throttle.c
@@ -1686,10 +1686,10 @@ static void tg_cancel_writeback_bios(struct throtl_grp *tg,
tg->flags |= THROTL_TG_CANCELING;
for (rw = READ; rw <= WRITE; rw++) {
- struct throtl_qnode *qn, *tmp;
+ struct throtl_qnode *qn;
unsigned int nr_bios = 0;
- list_for_each_entry_safe(qn, tmp, &sq->queued[rw], node) {
+ list_for_each_entry_mutable(qn, &sq->queued[rw], node) {
struct bio *bio;
while ((bio = bio_list_pop(&qn->bios_iops))) {
diff --git a/block/kyber-iosched.c b/block/kyber-iosched.c
index 971818bcdc9d..1a509666b861 100644
--- a/block/kyber-iosched.c
+++ b/block/kyber-iosched.c
@@ -578,9 +578,9 @@ static void kyber_insert_requests(struct blk_mq_hw_ctx *hctx,
blk_insert_t flags)
{
struct kyber_hctx_data *khd = hctx->sched_data;
- struct request *rq, *next;
+ struct request *rq;
- list_for_each_entry_safe(rq, next, rq_list, queuelist) {
+ list_for_each_entry_mutable(rq, rq_list, queuelist) {
unsigned int sched_domain = kyber_sched_domain(rq->cmd_flags);
struct kyber_ctx_queue *kcq = &khd->kcqs[rq->mq_ctx->index_hw[hctx->type]];
struct list_head *head = &kcq->rq_list[sched_domain];
diff --git a/block/partitions/ldm.c b/block/partitions/ldm.c
index c0bdcae58a3e..459f72f2148a 100644
--- a/block/partitions/ldm.c
+++ b/block/partitions/ldm.c
@@ -1285,11 +1285,11 @@ static bool ldm_frag_add (const u8 *data, int size, struct list_head *frags)
*/
static void ldm_frag_free (struct list_head *list)
{
- struct list_head *item, *tmp;
+ struct list_head *item;
BUG_ON (!list);
- list_for_each_safe (item, tmp, list)
+ list_for_each_mutable(item, list)
kfree (list_entry (item, struct frag, list));
}
@@ -1400,11 +1400,11 @@ static bool ldm_get_vblks(struct parsed_partitions *state, unsigned long base,
*/
static void ldm_free_vblks (struct list_head *lh)
{
- struct list_head *item, *tmp;
+ struct list_head *item;
BUG_ON (!lh);
- list_for_each_safe (item, tmp, lh)
+ list_for_each_mutable(item, lh)
kfree (list_entry (item, struct vblk, list));
}
diff --git a/block/sed-opal.c b/block/sed-opal.c
index 79b290d9458a..5bf9ebce8452 100644
--- a/block/sed-opal.c
+++ b/block/sed-opal.c
@@ -2573,10 +2573,10 @@ static int check_opal_support(struct opal_dev *dev)
static void clean_opal_dev(struct opal_dev *dev)
{
- struct opal_suspend_data *suspend, *next;
+ struct opal_suspend_data *suspend;
mutex_lock(&dev->dev_lock);
- list_for_each_entry_safe(suspend, next, &dev->unlk_lst, node) {
+ list_for_each_entry_mutable(suspend, &dev->unlk_lst, node) {
list_del(&suspend->node);
kfree(suspend);
}
--
2.43.0
^ permalink raw reply related
* [PATCH v3 5/7] kernel: Use mutable list iterators
From: Kaitao Cheng @ 2026-06-22 4:28 UTC (permalink / raw)
To: Paul Moore, Eric Paris, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
David S. Miller, Jakub Kicinski, Jesper Dangaard Brouer,
John Fastabend, Tejun Heo, Johannes Weiner, Michal Koutný,
Maarten Lankhorst, Maxime Ripard, Natalie Vock, Peter Zijlstra,
Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim,
Masami Hiramatsu, Oleg Nesterov, Peter Oberparleiter,
Andrew Morton, Baoquan He, Mike Rapoport, Pasha Tatashin,
Pratyush Yadav, Naveen N Rao, Josh Poimboeuf, Jiri Kosina,
Miroslav Benes, Petr Mladek, Will Deacon, Boqun Feng,
Luis Chamberlain, Petr Pavlu, Daniel Gomez, Sami Tolvanen,
Steffen Klassert, Daniel Jordan, Rafael J. Wysocki,
Davidlohr Bueso, Paul E. McKenney, Josh Triplett,
Frederic Weisbecker, Neeraj Upadhyay, Joel Fernandes,
Uladzislau Rezki, Juri Lelli, Vincent Guittot, Kees Cook,
Balbir Singh, Anna-Maria Behnsen, Thomas Gleixner, John Stultz,
KP Singh, Matt Bobrowski, Nathan Chancellor, Martin KaFai Lau,
Song Liu, Mark Rutland, Mathieu Desnoyers, Dietmar Eggemann,
David Vernet, Steven Rostedt
Cc: audit, linux-kernel, bpf, netdev, cgroups, dri-devel,
linux-perf-users, linux-trace-kernel, kexec, live-patching,
linux-modules, linux-crypto, linux-pm, rcu, sched-ext, llvm,
Kaitao Cheng
In-Reply-To: <20260622040533.29824-1-kaitao.cheng@linux.dev>
From: Kaitao Cheng <chengkaitao@kylinos.cn>
The safe list iteration helpers require callers to provide a temporary
cursor even when the cursor is only used internally by the loop. This
leaves many functions with otherwise unused variables whose only purpose
is to satisfy the old iterator interface.
Use the mutable list iteration helpers for those cases. The mutable
helpers keep the same removal-safe traversal semantics, while allowing
the temporary cursor to be internal to the macro when the caller does
not need to observe it.
Convert list, hlist and llist users under kernel/ where the temporary
cursor is not used outside the iteration. Keep the explicit cursor form
where the next entry is still needed by the surrounding code.
No functional change intended.
Signed-off-by: Kaitao Cheng <chengkaitao@kylinos.cn>
---
kernel/audit_tree.c | 4 +--
kernel/audit_watch.c | 16 ++++-----
kernel/auditfilter.c | 4 +--
kernel/auditsc.c | 4 +--
kernel/bpf/arena.c | 10 +++---
kernel/bpf/arraymap.c | 8 ++---
kernel/bpf/bpf_local_storage.c | 3 +-
kernel/bpf/bpf_lru_list.c | 25 ++++++-------
kernel/bpf/btf.c | 18 +++++-----
kernel/bpf/cgroup.c | 7 ++--
kernel/bpf/cpumap.c | 4 +--
kernel/bpf/devmap.c | 10 +++---
kernel/bpf/helpers.c | 8 ++---
kernel/bpf/local_storage.c | 4 +--
kernel/bpf/memalloc.c | 16 ++++-----
kernel/bpf/offload.c | 8 ++---
kernel/bpf/states.c | 4 +--
kernel/bpf/stream.c | 4 +--
kernel/bpf/verifier.c | 6 ++--
kernel/cgroup/cgroup-v1.c | 4 +--
kernel/cgroup/cgroup.c | 54 ++++++++++++++---------------
kernel/cgroup/dmem.c | 12 +++----
kernel/cgroup/rdma.c | 8 ++---
kernel/events/core.c | 44 +++++++++++------------
kernel/events/uprobes.c | 12 +++----
kernel/exit.c | 8 ++---
kernel/fail_function.c | 4 +--
kernel/gcov/clang.c | 4 +--
kernel/irq_work.c | 4 +--
kernel/kexec_core.c | 4 +--
kernel/kprobes.c | 16 ++++-----
kernel/livepatch/core.c | 4 +--
kernel/livepatch/core.h | 4 +--
kernel/liveupdate/kho_block.c | 4 +--
kernel/liveupdate/luo_flb.c | 4 +--
kernel/locking/rwsem.c | 2 +-
kernel/locking/test-ww_mutex.c | 2 +-
kernel/module/main.c | 11 +++---
kernel/padata.c | 4 +--
kernel/power/snapshot.c | 8 ++---
kernel/power/wakelock.c | 4 +--
kernel/printk/printk.c | 11 +++---
kernel/ptrace.c | 4 +--
kernel/rcu/rcutorture.c | 3 +-
kernel/rcu/tasks.h | 9 +++--
kernel/rcu/tree.c | 6 ++--
kernel/resource.c | 4 +--
kernel/sched/core.c | 4 +--
kernel/sched/ext.c | 22 ++++++------
kernel/sched/fair.c | 28 +++++++--------
kernel/sched/topology.c | 4 +--
kernel/sched/wait.c | 4 +--
kernel/seccomp.c | 4 +--
kernel/signal.c | 11 +++---
kernel/smp.c | 4 +--
kernel/taskstats.c | 8 ++---
kernel/time/clockevents.c | 6 ++--
kernel/time/clocksource.c | 4 +--
kernel/time/posix-cpu-timers.c | 4 +--
kernel/time/posix-timers.c | 3 +-
kernel/torture.c | 3 +-
kernel/trace/bpf_trace.c | 4 +--
kernel/trace/ftrace.c | 49 +++++++++++---------------
kernel/trace/ring_buffer.c | 25 +++++++------
kernel/trace/trace.c | 12 +++----
kernel/trace/trace_dynevent.c | 6 ++--
kernel/trace/trace_dynevent.h | 5 ++-
kernel/trace/trace_events.c | 35 +++++++++----------
kernel/trace/trace_events_filter.c | 4 +--
kernel/trace/trace_events_hist.c | 8 ++---
kernel/trace/trace_events_trigger.c | 17 ++++-----
kernel/trace/trace_events_user.c | 16 ++++-----
kernel/trace/trace_stat.c | 4 +--
kernel/user-return-notifier.c | 3 +-
kernel/workqueue.c | 16 ++++-----
75 files changed, 353 insertions(+), 381 deletions(-)
diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c
index 1ed19b775912..9652b0595ad4 100644
--- a/kernel/audit_tree.c
+++ b/kernel/audit_tree.c
@@ -543,10 +543,10 @@ static void audit_tree_log_remove_rule(struct audit_context *context,
static void kill_rules(struct audit_context *context, struct audit_tree *tree)
{
- struct audit_krule *rule, *next;
+ struct audit_krule *rule;
struct audit_entry *entry;
- list_for_each_entry_safe(rule, next, &tree->rules, rlist) {
+ list_for_each_entry_mutable(rule, &tree->rules, rlist) {
entry = container_of(rule, struct audit_entry, rule);
list_del_init(&rule->rlist);
diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c
index 06dd0ebe73e2..f56812c8186f 100644
--- a/kernel/audit_watch.c
+++ b/kernel/audit_watch.c
@@ -247,14 +247,14 @@ static void audit_update_watch(struct audit_parent *parent,
u64 ino, unsigned int invalidating,
struct audit_watch_ctx *ctx)
{
- struct audit_watch *owatch, *nwatch, *nextw;
- struct audit_krule *r, *nextr;
+ struct audit_watch *owatch, *nwatch;
+ struct audit_krule *r;
struct audit_entry *oentry, *nentry;
mutex_lock(&audit_filter_mutex);
/* Run all of the watches on this parent looking for the one that
* matches the given dname */
- list_for_each_entry_safe(owatch, nextw, &parent->watches, wlist) {
+ list_for_each_entry_mutable(owatch, &parent->watches, wlist) {
if (audit_compare_dname_path(dname, owatch->path,
AUDIT_NAME_FULL))
continue;
@@ -275,7 +275,7 @@ static void audit_update_watch(struct audit_parent *parent,
nwatch->dev = dev;
nwatch->ino = ino;
- list_for_each_entry_safe(r, nextr, &owatch->rules, rlist) {
+ list_for_each_entry_mutable(r, &owatch->rules, rlist) {
oentry = container_of(r, struct audit_entry, rule);
list_del(&oentry->rule.rlist);
@@ -322,13 +322,13 @@ static void audit_update_watch(struct audit_parent *parent,
/* Remove all watches & rules associated with a parent that is going away. */
static void audit_remove_parent_watches(struct audit_parent *parent)
{
- struct audit_watch *w, *nextw;
- struct audit_krule *r, *nextr;
+ struct audit_watch *w;
+ struct audit_krule *r;
struct audit_entry *e;
mutex_lock(&audit_filter_mutex);
- list_for_each_entry_safe(w, nextw, &parent->watches, wlist) {
- list_for_each_entry_safe(r, nextr, &w->rules, rlist) {
+ list_for_each_entry_mutable(w, &parent->watches, wlist) {
+ list_for_each_entry_mutable(r, &w->rules, rlist) {
e = container_of(r, struct audit_entry, rule);
audit_watch_log_rule_change(r, w, "remove_rule");
if (e->rule.exe)
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
index 4401119b5275..6a4936870903 100644
--- a/kernel/auditfilter.c
+++ b/kernel/auditfilter.c
@@ -1445,14 +1445,14 @@ static int update_lsm_rule(struct audit_krule *r)
* updated rule. */
int audit_update_lsm_rules(void)
{
- struct audit_krule *r, *n;
+ struct audit_krule *r;
int i, err = 0;
/* audit_filter_mutex synchronizes the writers */
mutex_lock(&audit_filter_mutex);
for (i = 0; i < AUDIT_NR_FILTERS; i++) {
- list_for_each_entry_safe(r, n, &audit_rules_list[i], list) {
+ list_for_each_entry_mutable(r, &audit_rules_list[i], list) {
int res = update_lsm_rule(r);
if (!err)
err = res;
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 6610e667c728..df9c6c9e9e49 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -929,9 +929,9 @@ static inline void audit_free_module(struct audit_context *context)
}
static inline void audit_free_names(struct audit_context *context)
{
- struct audit_names *n, *next;
+ struct audit_names *n;
- list_for_each_entry_safe(n, next, &context->names_list, list) {
+ list_for_each_entry_mutable(n, &context->names_list, list) {
list_del(&n->list);
if (n->name)
putname(n->name);
diff --git a/kernel/bpf/arena.c b/kernel/bpf/arena.c
index 80b7b8a69446..597c3cd428eb 100644
--- a/kernel/bpf/arena.c
+++ b/kernel/bpf/arena.c
@@ -842,7 +842,7 @@ static void arena_free_pages(struct bpf_arena *arena, long uaddr, long page_cnt,
long kaddr, pgoff;
struct page *page;
struct llist_head free_pages;
- struct llist_node *pos, *t;
+ struct llist_node *pos;
struct arena_free_span *s;
struct clear_range_data cdata;
unsigned long flags;
@@ -889,7 +889,7 @@ static void arena_free_pages(struct bpf_arena *arena, long uaddr, long page_cnt,
/* bulk zap if multiple pages being freed */
zap_pages(arena, full_uaddr, page_cnt);
- llist_for_each_safe(pos, t, __llist_del_all(&free_pages)) {
+ llist_for_each_mutable(pos, __llist_del_all(&free_pages)) {
page = llist_entry(pos, struct page, pcp_llist);
if (page_cnt == 1 && page_ref_count(page) > 1) /* maybe mapped by user space */
/* Optimization for the common case of page_cnt==1:
@@ -963,7 +963,7 @@ static void arena_free_worker(struct work_struct *work)
{
struct bpf_arena *arena = container_of(work, struct bpf_arena, free_work);
struct mem_cgroup *new_memcg, *old_memcg;
- struct llist_node *list, *pos, *t;
+ struct llist_node *list, *pos;
struct arena_free_span *s;
u64 arena_vm_start, user_vm_start;
struct llist_head free_pages;
@@ -1002,7 +1002,7 @@ static void arena_free_worker(struct work_struct *work)
raw_res_spin_unlock_irqrestore(&arena->spinlock, flags);
/* Iterate the list again without holding spinlock to do the tlb flush and zap_pages */
- llist_for_each_safe(pos, t, list) {
+ llist_for_each_mutable(pos, list) {
s = llist_entry(pos, struct arena_free_span, node);
page_cnt = s->page_cnt;
full_uaddr = clear_lo32(user_vm_start) + s->uaddr;
@@ -1018,7 +1018,7 @@ static void arena_free_worker(struct work_struct *work)
}
/* free all pages collected by apply_to_existing_page_range() in the first loop */
- llist_for_each_safe(pos, t, __llist_del_all(&free_pages)) {
+ llist_for_each_mutable(pos, __llist_del_all(&free_pages)) {
page = llist_entry(pos, struct page, pcp_llist);
__free_page(page);
}
diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
index 248b4818178c..1150179a90f7 100644
--- a/kernel/bpf/arraymap.c
+++ b/kernel/bpf/arraymap.c
@@ -1083,12 +1083,12 @@ static int prog_array_map_poke_track(struct bpf_map *map,
static void prog_array_map_poke_untrack(struct bpf_map *map,
struct bpf_prog_aux *prog_aux)
{
- struct prog_poke_elem *elem, *tmp;
+ struct prog_poke_elem *elem;
struct bpf_array_aux *aux;
aux = container_of(map, struct bpf_array, map)->aux;
mutex_lock(&aux->poke_mutex);
- list_for_each_entry_safe(elem, tmp, &aux->poke_progs, list) {
+ list_for_each_entry_mutable(elem, &aux->poke_progs, list) {
if (elem->aux == prog_aux) {
list_del_init(&elem->list);
kfree(elem);
@@ -1196,11 +1196,11 @@ static struct bpf_map *prog_array_map_alloc(union bpf_attr *attr)
static void prog_array_map_free(struct bpf_map *map)
{
- struct prog_poke_elem *elem, *tmp;
+ struct prog_poke_elem *elem;
struct bpf_array_aux *aux;
aux = container_of(map, struct bpf_array, map)->aux;
- list_for_each_entry_safe(elem, tmp, &aux->poke_progs, list) {
+ list_for_each_entry_mutable(elem, &aux->poke_progs, list) {
list_del_init(&elem->list);
kfree(elem);
}
diff --git a/kernel/bpf/bpf_local_storage.c b/kernel/bpf/bpf_local_storage.c
index 6fc6a4b672b5..5f01ba032b12 100644
--- a/kernel/bpf/bpf_local_storage.c
+++ b/kernel/bpf/bpf_local_storage.c
@@ -161,14 +161,13 @@ void bpf_selem_free(struct bpf_local_storage_elem *selem,
static void bpf_selem_free_list(struct hlist_head *list, bool reuse_now)
{
struct bpf_local_storage_elem *selem;
- struct hlist_node *n;
/* The "_safe" iteration is needed.
* The loop is not removing the selem from the list
* but bpf_selem_free will use the selem->rcu_head
* which is union-ized with the selem->free_node.
*/
- hlist_for_each_entry_safe(selem, n, list, free_node)
+ hlist_for_each_entry_mutable(selem, list, free_node)
bpf_selem_free(selem, reuse_now);
}
diff --git a/kernel/bpf/bpf_lru_list.c b/kernel/bpf/bpf_lru_list.c
index 5ed7cb4b98c0..4b7306ade684 100644
--- a/kernel/bpf/bpf_lru_list.c
+++ b/kernel/bpf/bpf_lru_list.c
@@ -126,11 +126,11 @@ static void __bpf_lru_list_rotate_active(struct bpf_lru *lru,
struct bpf_lru_list *l)
{
struct list_head *active = &l->lists[BPF_LRU_LIST_T_ACTIVE];
- struct bpf_lru_node *node, *tmp_node, *first_node;
+ struct bpf_lru_node *node, *first_node;
unsigned int i = 0;
first_node = list_first_entry(active, struct bpf_lru_node, list);
- list_for_each_entry_safe_reverse(node, tmp_node, active, list) {
+ list_for_each_entry_mutable_reverse(node, active, list) {
if (bpf_lru_node_is_ref(node))
__bpf_lru_node_move(l, node, BPF_LRU_LIST_T_ACTIVE);
else
@@ -196,11 +196,11 @@ __bpf_lru_list_shrink_inactive(struct bpf_lru *lru,
enum bpf_lru_list_type tgt_free_type)
{
struct list_head *inactive = &l->lists[BPF_LRU_LIST_T_INACTIVE];
- struct bpf_lru_node *node, *tmp_node;
+ struct bpf_lru_node *node;
unsigned int nshrinked = 0;
unsigned int i = 0;
- list_for_each_entry_safe_reverse(node, tmp_node, inactive, list) {
+ list_for_each_entry_mutable_reverse(node, inactive, list) {
if (bpf_lru_node_is_ref(node) &&
!READ_ONCE(node->pending_free)) {
__bpf_lru_node_move(l, node, BPF_LRU_LIST_T_ACTIVE);
@@ -247,7 +247,7 @@ static unsigned int __bpf_lru_list_shrink(struct bpf_lru *lru,
enum bpf_lru_list_type tgt_free_type)
{
- struct bpf_lru_node *node, *tmp_node;
+ struct bpf_lru_node *node;
struct list_head *force_shrink_list;
unsigned int nshrinked;
@@ -262,8 +262,7 @@ static unsigned int __bpf_lru_list_shrink(struct bpf_lru *lru,
else
force_shrink_list = &l->lists[BPF_LRU_LIST_T_ACTIVE];
- list_for_each_entry_safe_reverse(node, tmp_node, force_shrink_list,
- list) {
+ list_for_each_entry_mutable_reverse(node, force_shrink_list, list) {
if (READ_ONCE(node->pending_free) ||
lru->del_from_htab(lru->del_arg, node)) {
__bpf_lru_node_move_to_free(l, node, free_list,
@@ -279,10 +278,9 @@ static unsigned int __bpf_lru_list_shrink(struct bpf_lru *lru,
static void __local_list_flush(struct bpf_lru_list *l,
struct bpf_lru_locallist *loc_l)
{
- struct bpf_lru_node *node, *tmp_node;
+ struct bpf_lru_node *node;
- list_for_each_entry_safe_reverse(node, tmp_node,
- &loc_l->pending_list, list) {
+ list_for_each_entry_mutable_reverse(node, &loc_l->pending_list, list) {
if (READ_ONCE(node->pending_free))
__bpf_lru_node_move_in(l, node, BPF_LRU_LIST_T_FREE);
else if (bpf_lru_node_is_ref(node))
@@ -313,7 +311,7 @@ static void bpf_lru_list_pop_free_to_local(struct bpf_lru *lru,
struct bpf_lru_locallist *loc_l)
{
struct bpf_lru_list *l = &lru->common_lru.lru_list;
- struct bpf_lru_node *node, *tmp_node;
+ struct bpf_lru_node *node;
unsigned int nfree = 0;
LIST_HEAD(tmp_free);
@@ -324,8 +322,7 @@ static void bpf_lru_list_pop_free_to_local(struct bpf_lru *lru,
__bpf_lru_list_rotate(lru, l);
- list_for_each_entry_safe(node, tmp_node, &l->lists[BPF_LRU_LIST_T_FREE],
- list) {
+ list_for_each_entry_mutable(node, &l->lists[BPF_LRU_LIST_T_FREE], list) {
__bpf_lru_node_move_to_free(l, node, &tmp_free,
BPF_LRU_LOCAL_LIST_T_FREE);
if (++nfree == lru->target_free)
@@ -343,7 +340,7 @@ static void bpf_lru_list_pop_free_to_local(struct bpf_lru *lru,
* Transfer the harvested nodes from the temporary list_head into
* the lockless per-CPU free llist.
*/
- list_for_each_entry_safe(node, tmp_node, &tmp_free, list) {
+ list_for_each_entry_mutable(node, &tmp_free, list) {
list_del(&node->list);
llist_add(&node->llist, &loc_l->free_llist);
}
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 15ae7c43f594..983928bd774b 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -8439,7 +8439,7 @@ static void purge_cand_cache(struct btf *btf);
static int btf_module_notify(struct notifier_block *nb, unsigned long op,
void *module)
{
- struct btf_module *btf_mod, *tmp;
+ struct btf_module *btf_mod;
struct module *mod = module;
struct btf *btf;
int err = 0;
@@ -8512,7 +8512,7 @@ static int btf_module_notify(struct notifier_block *nb, unsigned long op,
break;
case MODULE_STATE_LIVE:
mutex_lock(&btf_module_mutex);
- list_for_each_entry_safe(btf_mod, tmp, &btf_modules, list) {
+ list_for_each_entry_mutable(btf_mod, &btf_modules, list) {
if (btf_mod->module != module)
continue;
@@ -8523,7 +8523,7 @@ static int btf_module_notify(struct notifier_block *nb, unsigned long op,
break;
case MODULE_STATE_GOING:
mutex_lock(&btf_module_mutex);
- list_for_each_entry_safe(btf_mod, tmp, &btf_modules, list) {
+ list_for_each_entry_mutable(btf_mod, &btf_modules, list) {
if (btf_mod->module != module)
continue;
@@ -8567,10 +8567,10 @@ struct module *btf_try_get_module(const struct btf *btf)
{
struct module *res = NULL;
#ifdef CONFIG_DEBUG_INFO_BTF_MODULES
- struct btf_module *btf_mod, *tmp;
+ struct btf_module *btf_mod;
mutex_lock(&btf_module_mutex);
- list_for_each_entry_safe(btf_mod, tmp, &btf_modules, list) {
+ list_for_each_entry_mutable(btf_mod, &btf_modules, list) {
if (btf_mod->btf != btf)
continue;
@@ -8596,7 +8596,7 @@ struct module *btf_try_get_module(const struct btf *btf)
static struct btf *btf_get_module_btf(const struct module *module)
{
#ifdef CONFIG_DEBUG_INFO_BTF_MODULES
- struct btf_module *btf_mod, *tmp;
+ struct btf_module *btf_mod;
#endif
struct btf *btf = NULL;
@@ -8609,7 +8609,7 @@ static struct btf *btf_get_module_btf(const struct module *module)
#ifdef CONFIG_DEBUG_INFO_BTF_MODULES
mutex_lock(&btf_module_mutex);
- list_for_each_entry_safe(btf_mod, tmp, &btf_modules, list) {
+ list_for_each_entry_mutable(btf_mod, &btf_modules, list) {
if (btf_mod->module != module)
continue;
@@ -8773,7 +8773,7 @@ static int btf_check_iter_kfuncs(struct btf *btf, const char *func_name,
static int btf_check_kfunc_name(struct btf *btf, const char *func_name, u32 kind)
{
#ifdef CONFIG_DEBUG_INFO_BTF_MODULES
- struct btf_module *btf_mod, *tmp;
+ struct btf_module *btf_mod;
#endif
s32 id;
@@ -8789,7 +8789,7 @@ static int btf_check_kfunc_name(struct btf *btf, const char *func_name, u32 kind
#ifdef CONFIG_DEBUG_INFO_BTF_MODULES
guard(mutex)(&btf_module_mutex);
- list_for_each_entry_safe(btf_mod, tmp, &btf_modules, list) {
+ list_for_each_entry_mutable(btf_mod, &btf_modules, list) {
if (btf_mod->btf == btf)
continue;
id = btf_find_by_name_kind(btf_mod->btf, func_name, kind);
diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c
index 83ce66296ac1..a3bd18ab9246 100644
--- a/kernel/bpf/cgroup.c
+++ b/kernel/bpf/cgroup.c
@@ -328,7 +328,7 @@ static void cgroup_bpf_release(struct work_struct *work)
bpf.release_work);
struct bpf_prog_array *old_array;
struct list_head *storages = &cgrp->bpf.storages;
- struct bpf_cgroup_storage *storage, *stmp;
+ struct bpf_cgroup_storage *storage;
unsigned int atype;
@@ -337,9 +337,8 @@ static void cgroup_bpf_release(struct work_struct *work)
for (atype = 0; atype < ARRAY_SIZE(cgrp->bpf.progs); atype++) {
struct hlist_head *progs = &cgrp->bpf.progs[atype];
struct bpf_prog_list *pl;
- struct hlist_node *pltmp;
- hlist_for_each_entry_safe(pl, pltmp, progs, node) {
+ hlist_for_each_entry_mutable(pl, progs, node) {
hlist_del(&pl->node);
if (pl->prog) {
if (pl->prog->expected_attach_type == BPF_LSM_CGROUP)
@@ -360,7 +359,7 @@ static void cgroup_bpf_release(struct work_struct *work)
bpf_prog_array_free(old_array);
}
- list_for_each_entry_safe(storage, stmp, storages, list_cg) {
+ list_for_each_entry_mutable(storage, storages, list_cg) {
bpf_cgroup_storage_unlink(storage);
bpf_cgroup_storage_free(storage);
}
diff --git a/kernel/bpf/cpumap.c b/kernel/bpf/cpumap.c
index 5e59ab896f05..fa3a1b3559e2 100644
--- a/kernel/bpf/cpumap.c
+++ b/kernel/bpf/cpumap.c
@@ -821,9 +821,9 @@ int cpu_map_generic_redirect(struct bpf_cpu_map_entry *rcpu,
void __cpu_map_flush(struct list_head *flush_list)
{
- struct xdp_bulk_queue *bq, *tmp;
+ struct xdp_bulk_queue *bq;
- list_for_each_entry_safe(bq, tmp, flush_list, flush_node) {
+ list_for_each_entry_mutable(bq, flush_list, flush_node) {
local_lock_nested_bh(&bq->obj->bulkq->bq_lock);
bq_flush_to_queue(bq);
local_unlock_nested_bh(&bq->obj->bulkq->bq_lock);
diff --git a/kernel/bpf/devmap.c b/kernel/bpf/devmap.c
index dc7b859e8bbf..d85e4f955061 100644
--- a/kernel/bpf/devmap.c
+++ b/kernel/bpf/devmap.c
@@ -219,11 +219,10 @@ static void dev_map_free(struct bpf_map *map)
for (i = 0; i < dtab->n_buckets; i++) {
struct bpf_dtab_netdev *dev;
struct hlist_head *head;
- struct hlist_node *next;
head = dev_map_index_hash(dtab, i);
- hlist_for_each_entry_safe(dev, next, head, index_hlist) {
+ hlist_for_each_entry_mutable(dev, head, index_hlist) {
hlist_del_rcu(&dev->index_hlist);
if (dev->xdp_prog)
bpf_prog_put(dev->xdp_prog);
@@ -426,9 +425,9 @@ static void bq_xmit_all(struct xdp_dev_bulk_queue *bq, u32 flags)
*/
void __dev_flush(struct list_head *flush_list)
{
- struct xdp_dev_bulk_queue *bq, *tmp;
+ struct xdp_dev_bulk_queue *bq;
- list_for_each_entry_safe(bq, tmp, flush_list, flush_node) {
+ list_for_each_entry_mutable(bq, flush_list, flush_node) {
local_lock_nested_bh(&bq->dev->xdp_bulkq->bq_lock);
bq_xmit_all(bq, XDP_XMIT_FLUSH);
bq->dev_rx = NULL;
@@ -1124,11 +1123,10 @@ static void dev_map_hash_remove_netdev(struct bpf_dtab *dtab,
for (i = 0; i < dtab->n_buckets; i++) {
struct bpf_dtab_netdev *dev;
struct hlist_head *head;
- struct hlist_node *next;
head = dev_map_index_hash(dtab, i);
- hlist_for_each_entry_safe(dev, next, head, index_hlist) {
+ hlist_for_each_entry_mutable(dev, head, index_hlist) {
if (netdev != dev->dev)
continue;
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index c18f1e16edee..cfaf97dd970a 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -1662,7 +1662,7 @@ static void bpf_async_process_op(struct bpf_async_cb *cb, u32 op,
static void bpf_async_irq_worker(struct irq_work *work)
{
struct bpf_async_cb *cb = container_of(work, struct bpf_async_cb, worker);
- struct llist_node *pos, *n, *list;
+ struct llist_node *pos, *list;
list = llist_del_all(&cb->async_cmds);
if (!list)
@@ -1670,7 +1670,7 @@ static void bpf_async_irq_worker(struct irq_work *work)
list = llist_reverse_order(list);
this_cpu_write(async_cb_running, cb);
- llist_for_each_safe(pos, n, list) {
+ llist_for_each_mutable(pos, list) {
struct bpf_async_cmd *cmd;
cmd = container_of(pos, struct bpf_async_cmd, node);
@@ -2247,7 +2247,7 @@ EXPORT_SYMBOL_GPL(bpf_base_func_proto);
void bpf_list_head_free(const struct btf_field *field, void *list_head,
struct bpf_spin_lock *spin_lock)
{
- struct list_head *head = list_head, drain, *pos, *n;
+ struct list_head *head = list_head, drain, *pos;
BUILD_BUG_ON(sizeof(struct list_head) > sizeof(struct bpf_list_head));
BUILD_BUG_ON(__alignof__(struct list_head) > __alignof__(struct bpf_list_head));
@@ -2262,7 +2262,7 @@ void bpf_list_head_free(const struct btf_field *field, void *list_head,
__bpf_spin_lock_irqsave(spin_lock);
if (!head->next || list_empty(head))
goto unlock;
- list_for_each_safe(pos, n, head) {
+ list_for_each_mutable(pos, head) {
struct bpf_list_node_kern *node;
node = container_of(pos, struct bpf_list_node_kern, list_head);
diff --git a/kernel/bpf/local_storage.c b/kernel/bpf/local_storage.c
index 23267213a17f..2595c5a4d171 100644
--- a/kernel/bpf/local_storage.c
+++ b/kernel/bpf/local_storage.c
@@ -342,11 +342,11 @@ static void cgroup_storage_map_free(struct bpf_map *_map)
{
struct bpf_cgroup_storage_map *map = map_to_storage(_map);
struct list_head *storages = &map->list;
- struct bpf_cgroup_storage *storage, *stmp;
+ struct bpf_cgroup_storage *storage;
cgroup_lock();
- list_for_each_entry_safe(storage, stmp, storages, list_map) {
+ list_for_each_entry_mutable(storage, storages, list_map) {
bpf_cgroup_storage_unlink(storage);
bpf_cgroup_storage_free(storage);
}
diff --git a/kernel/bpf/memalloc.c b/kernel/bpf/memalloc.c
index e9662db7198f..09adb1d0e101 100644
--- a/kernel/bpf/memalloc.c
+++ b/kernel/bpf/memalloc.c
@@ -264,10 +264,10 @@ static void free_one(void *obj, bool percpu)
static int free_all(struct bpf_mem_cache *c, struct llist_node *llnode, bool percpu)
{
- struct llist_node *pos, *t;
+ struct llist_node *pos;
int cnt = 0;
- llist_for_each_safe(pos, t, llnode) {
+ llist_for_each_mutable(pos, llnode) {
if (c->dtor)
c->dtor((void *)pos + LLIST_NODE_SZ, c->dtor_ctx);
free_one(pos, percpu);
@@ -296,7 +296,7 @@ static void enque_to_free(struct bpf_mem_cache *c, void *obj)
static void do_call_rcu_ttrace(struct bpf_mem_cache *c)
{
- struct llist_node *llnode, *t;
+ struct llist_node *llnode;
if (atomic_xchg(&c->call_rcu_ttrace_in_progress, 1)) {
if (unlikely(READ_ONCE(c->draining))) {
@@ -307,7 +307,7 @@ static void do_call_rcu_ttrace(struct bpf_mem_cache *c)
}
WARN_ON_ONCE(!llist_empty(&c->waiting_for_gp_ttrace));
- llist_for_each_safe(llnode, t, llist_del_all(&c->free_by_rcu_ttrace))
+ llist_for_each_mutable(llnode, llist_del_all(&c->free_by_rcu_ttrace))
llist_add(llnode, &c->waiting_for_gp_ttrace);
if (unlikely(READ_ONCE(c->draining))) {
@@ -326,7 +326,7 @@ static void do_call_rcu_ttrace(struct bpf_mem_cache *c)
static void free_bulk(struct bpf_mem_cache *c)
{
struct bpf_mem_cache *tgt = c->tgt;
- struct llist_node *llnode, *t;
+ struct llist_node *llnode;
unsigned long flags;
int cnt;
@@ -346,7 +346,7 @@ static void free_bulk(struct bpf_mem_cache *c)
} while (cnt > (c->high_watermark + c->low_watermark) / 2);
/* and drain free_llist_extra */
- llist_for_each_safe(llnode, t, llist_del_all(&c->free_llist_extra))
+ llist_for_each_mutable(llnode, llist_del_all(&c->free_llist_extra))
enque_to_free(tgt, llnode);
do_call_rcu_ttrace(tgt);
}
@@ -374,13 +374,13 @@ static void __free_by_rcu(struct rcu_head *head)
static void check_free_by_rcu(struct bpf_mem_cache *c)
{
- struct llist_node *llnode, *t;
+ struct llist_node *llnode;
unsigned long flags;
/* drain free_llist_extra_rcu */
if (unlikely(!llist_empty(&c->free_llist_extra_rcu))) {
inc_active(c, &flags);
- llist_for_each_safe(llnode, t, llist_del_all(&c->free_llist_extra_rcu))
+ llist_for_each_mutable(llnode, llist_del_all(&c->free_llist_extra_rcu))
if (__llist_add(llnode, &c->free_by_rcu))
c->free_by_rcu_tail = llnode;
dec_active(c, &flags);
diff --git a/kernel/bpf/offload.c b/kernel/bpf/offload.c
index 0d6f5569588c..32a21613fe79 100644
--- a/kernel/bpf/offload.c
+++ b/kernel/bpf/offload.c
@@ -137,8 +137,8 @@ static void __bpf_offload_dev_netdev_unregister(struct bpf_offload_dev *offdev,
struct net_device *netdev)
{
struct bpf_offload_netdev *ondev, *altdev = NULL;
- struct bpf_offloaded_map *offmap, *mtmp;
- struct bpf_prog_offload *offload, *ptmp;
+ struct bpf_offloaded_map *offmap;
+ struct bpf_prog_offload *offload;
ASSERT_RTNL();
@@ -165,9 +165,9 @@ static void __bpf_offload_dev_netdev_unregister(struct bpf_offload_dev *offdev,
offmap->netdev = altdev->netdev;
list_splice_init(&ondev->maps, &altdev->maps);
} else {
- list_for_each_entry_safe(offload, ptmp, &ondev->progs, offloads)
+ list_for_each_entry_mutable(offload, &ondev->progs, offloads)
__bpf_prog_offload_destroy(offload->prog);
- list_for_each_entry_safe(offmap, mtmp, &ondev->maps, offloads)
+ list_for_each_entry_mutable(offmap, &ondev->maps, offloads)
__bpf_map_offload_destroy(offmap);
}
diff --git a/kernel/bpf/states.c b/kernel/bpf/states.c
index 32f346ce3ffc..ec7942049c06 100644
--- a/kernel/bpf/states.c
+++ b/kernel/bpf/states.c
@@ -1241,7 +1241,7 @@ int bpf_is_state_visited(struct bpf_verifier_env *env, int insn_idx)
struct bpf_verifier_state *cur = env->cur_state, *new;
bool force_new_state, add_new_state, loop;
int n, err, states_cnt = 0;
- struct list_head *pos, *tmp, *head;
+ struct list_head *pos, *head;
force_new_state = env->test_state_freq || bpf_is_force_checkpoint(env, insn_idx) ||
/* Avoid accumulating infinitely long jmp history */
@@ -1267,7 +1267,7 @@ int bpf_is_state_visited(struct bpf_verifier_env *env, int insn_idx)
loop = false;
head = bpf_explored_state(env, insn_idx);
- list_for_each_safe(pos, tmp, head) {
+ list_for_each_mutable(pos, head) {
sl = container_of(pos, struct bpf_verifier_state_list, node);
states_cnt++;
if (sl->state.insn_idx != insn_idx)
diff --git a/kernel/bpf/stream.c b/kernel/bpf/stream.c
index be9ce98e9469..3d722cfd1d07 100644
--- a/kernel/bpf/stream.c
+++ b/kernel/bpf/stream.c
@@ -96,9 +96,9 @@ static void bpf_stream_free_elem(struct bpf_stream_elem *elem)
static void bpf_stream_free_list(struct llist_node *list)
{
- struct bpf_stream_elem *elem, *tmp;
+ struct bpf_stream_elem *elem;
- llist_for_each_entry_safe(elem, tmp, list, node)
+ llist_for_each_entry_mutable(elem, list, node)
bpf_stream_free_elem(elem);
}
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 2abc79dbf281..7cd5d10b390a 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -18285,7 +18285,7 @@ static void sanitize_dead_code(struct bpf_verifier_env *env)
static void free_states(struct bpf_verifier_env *env)
{
struct bpf_verifier_state_list *sl;
- struct list_head *head, *pos, *tmp;
+ struct list_head *head, *pos;
struct bpf_scc_info *info;
int i, j;
@@ -18293,7 +18293,7 @@ static void free_states(struct bpf_verifier_env *env)
env->cur_state = NULL;
while (!pop_stack(env, NULL, NULL, false));
- list_for_each_safe(pos, tmp, &env->free_list) {
+ list_for_each_mutable(pos, &env->free_list) {
sl = container_of(pos, struct bpf_verifier_state_list, node);
bpf_free_verifier_state(&sl->state, false);
kfree(sl);
@@ -18316,7 +18316,7 @@ static void free_states(struct bpf_verifier_env *env)
for (i = 0; i < state_htab_size(env); i++) {
head = &env->explored_states[i];
- list_for_each_safe(pos, tmp, head) {
+ list_for_each_mutable(pos, head) {
sl = container_of(pos, struct bpf_verifier_state_list, node);
bpf_free_verifier_state(&sl->state, false);
kfree(sl);
diff --git a/kernel/cgroup/cgroup-v1.c b/kernel/cgroup/cgroup-v1.c
index a4337c9b5287..1c777b28861f 100644
--- a/kernel/cgroup/cgroup-v1.c
+++ b/kernel/cgroup/cgroup-v1.c
@@ -205,10 +205,10 @@ struct cgroup_pidlist {
*/
void cgroup1_pidlist_destroy_all(struct cgroup *cgrp)
{
- struct cgroup_pidlist *l, *tmp_l;
+ struct cgroup_pidlist *l;
mutex_lock(&cgrp->pidlist_mutex);
- list_for_each_entry_safe(l, tmp_l, &cgrp->pidlists, links)
+ list_for_each_entry_mutable(l, &cgrp->pidlists, links)
mod_delayed_work(cgroup_pidlist_destroy_wq, &l->destroy_dwork, 0);
mutex_unlock(&cgrp->pidlist_mutex);
diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c
index 38f8d9df8fbc..2b619c1553ee 100644
--- a/kernel/cgroup/cgroup.c
+++ b/kernel/cgroup/cgroup.c
@@ -872,9 +872,9 @@ static void css_set_update_populated(struct css_set *cset, bool populated)
static void css_set_skip_task_iters(struct css_set *cset,
struct task_struct *task)
{
- struct css_task_iter *it, *pos;
+ struct css_task_iter *it;
- list_for_each_entry_safe(it, pos, &cset->task_iters, iters_node)
+ list_for_each_entry_mutable(it, &cset->task_iters, iters_node)
css_task_iter_skip(it, task);
}
@@ -951,7 +951,7 @@ static unsigned long css_set_hash(struct cgroup_subsys_state **css)
void put_css_set_locked(struct css_set *cset)
{
- struct cgrp_cset_link *link, *tmp_link;
+ struct cgrp_cset_link *link;
struct cgroup_subsys *ss;
int ssid;
@@ -970,7 +970,7 @@ void put_css_set_locked(struct css_set *cset)
hash_del(&cset->hlist);
css_set_count--;
- list_for_each_entry_safe(link, tmp_link, &cset->cgrp_links, cgrp_link) {
+ list_for_each_entry_mutable(link, &cset->cgrp_links, cgrp_link) {
list_del(&link->cset_link);
list_del(&link->cgrp_link);
if (cgroup_parent(link->cgrp))
@@ -1129,9 +1129,9 @@ static struct css_set *find_existing_css_set(struct css_set *old_cset,
static void free_cgrp_cset_links(struct list_head *links_to_free)
{
- struct cgrp_cset_link *link, *tmp_link;
+ struct cgrp_cset_link *link;
- list_for_each_entry_safe(link, tmp_link, links_to_free, cset_link) {
+ list_for_each_entry_mutable(link, links_to_free, cset_link) {
list_del(&link->cset_link);
kfree(link);
}
@@ -1372,7 +1372,7 @@ void cgroup_free_root(struct cgroup_root *root)
static void cgroup_destroy_root(struct cgroup_root *root)
{
struct cgroup *cgrp = &root->cgrp;
- struct cgrp_cset_link *link, *tmp_link;
+ struct cgrp_cset_link *link;
int ret;
trace_cgroup_destroy_root(root);
@@ -1395,7 +1395,7 @@ static void cgroup_destroy_root(struct cgroup_root *root)
*/
spin_lock_irq(&css_set_lock);
- list_for_each_entry_safe(link, tmp_link, &cgrp->cset_links, cset_link) {
+ list_for_each_entry_mutable(link, &cgrp->cset_links, cset_link) {
list_del(&link->cset_link);
list_del(&link->cgrp_link);
kfree(link);
@@ -1887,7 +1887,7 @@ int rebind_subsystems(struct cgroup_root *dst_root, u32 ss_mask)
struct cgroup_root *src_root = ss->root;
struct cgroup *scgrp = &src_root->cgrp;
struct cgroup_subsys_state *css = cgroup_css(scgrp, ss);
- struct css_set *cset, *cset_pos;
+ struct css_set *cset;
struct css_task_iter *it;
WARN_ON(!css || cgroup_css(dcgrp, ss));
@@ -1912,8 +1912,8 @@ int rebind_subsystems(struct cgroup_root *dst_root, u32 ss_mask)
spin_lock_irq(&css_set_lock);
css->cgroup = dcgrp;
WARN_ON(!list_empty(&dcgrp->e_csets[ss->id]));
- list_for_each_entry_safe(cset, cset_pos, &scgrp->e_csets[ss->id],
- e_cset_node[ss->id]) {
+ list_for_each_entry_mutable(cset, &scgrp->e_csets[ss->id],
+ e_cset_node[ss->id]) {
list_move_tail(&cset->e_cset_node[ss->id],
&dcgrp->e_csets[ss->id]);
/*
@@ -2689,8 +2689,8 @@ static int cgroup_migrate_execute(struct cgroup_mgctx *mgctx)
{
struct cgroup_taskset *tset = &mgctx->tset;
struct cgroup_subsys *ss;
- struct task_struct *task, *tmp_task;
- struct css_set *cset, *tmp_cset;
+ struct task_struct *task;
+ struct css_set *cset;
int ssid, failed_ssid, ret;
/* check that we can legitimately attach to the cgroup */
@@ -2714,7 +2714,7 @@ static int cgroup_migrate_execute(struct cgroup_mgctx *mgctx)
*/
spin_lock_irq(&css_set_lock);
list_for_each_entry(cset, &tset->src_csets, mg_node) {
- list_for_each_entry_safe(task, tmp_task, &cset->mg_tasks, cg_list) {
+ list_for_each_entry_mutable(task, &cset->mg_tasks, cg_list) {
struct css_set *from_cset = task_css_set(task);
struct css_set *to_cset = cset->mg_dst_cset;
@@ -2767,7 +2767,7 @@ static int cgroup_migrate_execute(struct cgroup_mgctx *mgctx)
out_release_tset:
spin_lock_irq(&css_set_lock);
list_splice_init(&tset->dst_csets, &tset->src_csets);
- list_for_each_entry_safe(cset, tmp_cset, &tset->src_csets, mg_node) {
+ list_for_each_entry_mutable(cset, &tset->src_csets, mg_node) {
list_splice_tail_init(&cset->mg_tasks, &cset->tasks);
list_del_init(&cset->mg_node);
}
@@ -2825,14 +2825,14 @@ int cgroup_migrate_vet_dst(struct cgroup *dst_cgrp)
*/
void cgroup_migrate_finish(struct cgroup_mgctx *mgctx)
{
- struct css_set *cset, *tmp_cset;
+ struct css_set *cset;
lockdep_assert_held(&cgroup_mutex);
spin_lock_irq(&css_set_lock);
- list_for_each_entry_safe(cset, tmp_cset, &mgctx->preloaded_src_csets,
- mg_src_preload_node) {
+ list_for_each_entry_mutable(cset, &mgctx->preloaded_src_csets,
+ mg_src_preload_node) {
cset->mg_src_cgrp = NULL;
cset->mg_dst_cgrp = NULL;
cset->mg_dst_cset = NULL;
@@ -2840,8 +2840,8 @@ void cgroup_migrate_finish(struct cgroup_mgctx *mgctx)
put_css_set_locked(cset);
}
- list_for_each_entry_safe(cset, tmp_cset, &mgctx->preloaded_dst_csets,
- mg_dst_preload_node) {
+ list_for_each_entry_mutable(cset, &mgctx->preloaded_dst_csets,
+ mg_dst_preload_node) {
cset->mg_src_cgrp = NULL;
cset->mg_dst_cgrp = NULL;
cset->mg_dst_cset = NULL;
@@ -2917,13 +2917,13 @@ void cgroup_migrate_add_src(struct css_set *src_cset,
*/
int cgroup_migrate_prepare_dst(struct cgroup_mgctx *mgctx)
{
- struct css_set *src_cset, *tmp_cset;
+ struct css_set *src_cset;
lockdep_assert_held(&cgroup_mutex);
/* look up the dst cset for each src cset and link it to src */
- list_for_each_entry_safe(src_cset, tmp_cset, &mgctx->preloaded_src_csets,
- mg_src_preload_node) {
+ list_for_each_entry_mutable(src_cset, &mgctx->preloaded_src_csets,
+ mg_src_preload_node) {
struct css_set *dst_cset;
struct cgroup_subsys *ss;
int ssid;
@@ -3225,10 +3225,10 @@ static int cgroup_update_dfl_csses(struct cgroup *cgrp)
spin_lock_irq(&css_set_lock);
list_for_each_entry(src_cset, &mgctx.preloaded_src_csets,
mg_src_preload_node) {
- struct task_struct *task, *ntask;
+ struct task_struct *task;
/* all tasks in src_csets need to be migrated */
- list_for_each_entry_safe(task, ntask, &src_cset->tasks, cg_list)
+ list_for_each_entry_mutable(task, &src_cset->tasks, cg_list)
cgroup_migrate_add_task(task, &mgctx);
}
spin_unlock_irq(&css_set_lock);
@@ -7106,10 +7106,10 @@ static DEFINE_PER_CPU(struct irq_work, cgrp_dead_tasks_iwork);
static void cgrp_dead_tasks_iwork_fn(struct irq_work *iwork)
{
struct llist_node *lnode;
- struct task_struct *task, *next;
+ struct task_struct *task;
lnode = llist_del_all(this_cpu_ptr(&cgrp_dead_tasks));
- llist_for_each_entry_safe(task, next, lnode, cg_dead_lnode) {
+ llist_for_each_entry_mutable(task, lnode, cg_dead_lnode) {
do_cgroup_task_dead(task);
put_task_struct(task);
}
diff --git a/kernel/cgroup/dmem.c b/kernel/cgroup/dmem.c
index 4753a67d0f0f..1daa8fb49fbe 100644
--- a/kernel/cgroup/dmem.c
+++ b/kernel/cgroup/dmem.c
@@ -203,10 +203,10 @@ static void dmemcs_offline(struct cgroup_subsys_state *css)
static void dmemcs_free(struct cgroup_subsys_state *css)
{
struct dmemcg_state *dmemcs = css_to_dmemcs(css);
- struct dmem_cgroup_pool_state *pool, *next;
+ struct dmem_cgroup_pool_state *pool;
spin_lock(&dmemcg_lock);
- list_for_each_entry_safe(pool, next, &dmemcs->pools, css_node) {
+ list_for_each_entry_mutable(pool, &dmemcs->pools, css_node) {
/*
*The pool is dead and all references are 0,
* no need for RCU protection with list_del_rcu or freeing.
@@ -444,9 +444,9 @@ get_cg_pool_locked(struct dmemcg_state *dmemcs, struct dmem_cgroup_region *regio
static void dmemcg_free_rcu(struct rcu_head *rcu)
{
struct dmem_cgroup_region *region = container_of(rcu, typeof(*region), rcu);
- struct dmem_cgroup_pool_state *pool, *next;
+ struct dmem_cgroup_pool_state *pool;
- list_for_each_entry_safe(pool, next, ®ion->pools, region_node)
+ list_for_each_entry_mutable(pool, ®ion->pools, region_node)
free_cg_pool(pool);
kfree(region->name);
kfree(region);
@@ -467,7 +467,7 @@ static void dmemcg_free_region(struct kref *ref)
*/
void dmem_cgroup_unregister_region(struct dmem_cgroup_region *region)
{
- struct dmem_cgroup_pool_state *pool, *next;
+ struct dmem_cgroup_pool_state *pool;
if (!region)
return;
@@ -477,7 +477,7 @@ void dmem_cgroup_unregister_region(struct dmem_cgroup_region *region)
/* Remove from global region list */
list_del_rcu(®ion->region_node);
- list_for_each_entry_safe(pool, next, ®ion->pools, region_node) {
+ list_for_each_entry_mutable(pool, ®ion->pools, region_node) {
list_del_rcu(&pool->css_node);
list_del(&pool->region_node);
dmemcg_pool_put(pool);
diff --git a/kernel/cgroup/rdma.c b/kernel/cgroup/rdma.c
index 5e82a03b3270..5527375c16e1 100644
--- a/kernel/cgroup/rdma.c
+++ b/kernel/cgroup/rdma.c
@@ -444,7 +444,7 @@ EXPORT_SYMBOL(rdmacg_register_device);
*/
void rdmacg_unregister_device(struct rdmacg_device *device)
{
- struct rdmacg_resource_pool *rpool, *tmp;
+ struct rdmacg_resource_pool *rpool;
/*
* Synchronize with any active resource settings,
@@ -457,7 +457,7 @@ void rdmacg_unregister_device(struct rdmacg_device *device)
* Now that this device is off the cgroup list, its safe to free
* all the rpool resources.
*/
- list_for_each_entry_safe(rpool, tmp, &device->rpools, dev_node)
+ list_for_each_entry_mutable(rpool, &device->rpools, dev_node)
free_cg_rpool_locked(rpool);
mutex_unlock(&rdmacg_mutex);
@@ -747,11 +747,11 @@ rdmacg_css_alloc(struct cgroup_subsys_state *parent)
static void rdmacg_css_free(struct cgroup_subsys_state *css)
{
struct rdma_cgroup *cg = css_rdmacg(css);
- struct rdmacg_resource_pool *rpool, *tmp;
+ struct rdmacg_resource_pool *rpool;
/* Clean up rpools kept alive by non-zero peak values */
mutex_lock(&rdmacg_mutex);
- list_for_each_entry_safe(rpool, tmp, &cg->rpools, cg_node)
+ list_for_each_entry_mutable(rpool, &cg->rpools, cg_node)
free_cg_rpool_locked(rpool);
mutex_unlock(&rdmacg_mutex);
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 00e6dcb931d9..dd93436f8f2d 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -2346,7 +2346,7 @@ static inline struct list_head *get_event_list(struct perf_event *event)
static void perf_group_detach(struct perf_event *event)
{
struct perf_event *leader = event->group_leader;
- struct perf_event *sibling, *tmp;
+ struct perf_event *sibling;
struct perf_event_context *ctx = event->ctx;
lockdep_assert_held(&ctx->lock);
@@ -2376,7 +2376,7 @@ static void perf_group_detach(struct perf_event *event)
* upgrade the siblings to singleton events by adding them
* to whatever list we are on.
*/
- list_for_each_entry_safe(sibling, tmp, &event->sibling_list, sibling_list) {
+ list_for_each_entry_mutable(sibling, &event->sibling_list, sibling_list) {
/*
* Events that have PERF_EV_CAP_SIBLING require being part of
@@ -2405,8 +2405,8 @@ static void perf_group_detach(struct perf_event *event)
}
out:
- for_each_sibling_event(tmp, leader)
- perf_event__header_size(tmp);
+ for_each_sibling_event(sibling, leader)
+ perf_event__header_size(sibling);
perf_event__header_size(leader);
}
@@ -3528,7 +3528,7 @@ static void __pmu_ctx_sched_out(struct perf_event_pmu_context *pmu_ctx,
enum event_type_t event_type)
{
struct perf_event_context *ctx = pmu_ctx->ctx;
- struct perf_event *event, *tmp;
+ struct perf_event *event;
struct pmu *pmu = pmu_ctx->pmu;
if (ctx->task && !(ctx->is_active & EVENT_ALL)) {
@@ -3543,16 +3543,14 @@ static void __pmu_ctx_sched_out(struct perf_event_pmu_context *pmu_ctx,
perf_pmu_disable(pmu);
if (event_type & EVENT_PINNED) {
- list_for_each_entry_safe(event, tmp,
- &pmu_ctx->pinned_active,
- active_list)
+ list_for_each_entry_mutable(event, &pmu_ctx->pinned_active,
+ active_list)
group_sched_out(event, ctx);
}
if (event_type & EVENT_FLEXIBLE) {
- list_for_each_entry_safe(event, tmp,
- &pmu_ctx->flexible_active,
- active_list)
+ list_for_each_entry_mutable(event, &pmu_ctx->flexible_active,
+ active_list)
group_sched_out(event, ctx);
/*
* Since we cleared EVENT_FLEXIBLE, also clear
@@ -4738,7 +4736,7 @@ static void perf_event_exit_event(struct perf_event *event,
static void perf_event_remove_on_exec(struct perf_event_context *ctx)
{
struct perf_event_context *clone_ctx = NULL;
- struct perf_event *event, *next;
+ struct perf_event *event;
unsigned long flags;
bool modified = false;
@@ -4747,7 +4745,7 @@ static void perf_event_remove_on_exec(struct perf_event_context *ctx)
if (WARN_ON_ONCE(ctx->task != current))
goto unlock;
- list_for_each_entry_safe(event, next, &ctx->event_list, event_entry) {
+ list_for_each_entry_mutable(event, &ctx->event_list, event_entry) {
if (!event->attr.remove_on_exec)
continue;
@@ -11833,9 +11831,9 @@ perf_addr_filter_new(struct perf_event *event, struct list_head *filters)
static void free_filters_list(struct list_head *filters)
{
- struct perf_addr_filter *filter, *iter;
+ struct perf_addr_filter *filter;
- list_for_each_entry_safe(filter, iter, filters, entry) {
+ list_for_each_entry_mutable(filter, filters, entry) {
path_put(&filter->path);
list_del(&filter->entry);
kfree(filter);
@@ -14436,7 +14434,7 @@ static void __perf_pmu_install_event(struct pmu *pmu,
static void __perf_pmu_install(struct perf_event_context *ctx,
int cpu, struct pmu *pmu, struct list_head *events)
{
- struct perf_event *event, *tmp;
+ struct perf_event *event;
/*
* Re-instate events in 2 passes.
@@ -14446,7 +14444,7 @@ static void __perf_pmu_install(struct perf_event_context *ctx,
* leader will enable its siblings, even if those are still on the old
* context.
*/
- list_for_each_entry_safe(event, tmp, events, migrate_entry) {
+ list_for_each_entry_mutable(event, events, migrate_entry) {
if (event->group_leader == event)
continue;
@@ -14458,7 +14456,7 @@ static void __perf_pmu_install(struct perf_event_context *ctx,
* Once all the siblings are setup properly, install the group leaders
* to make it go.
*/
- list_for_each_entry_safe(event, tmp, events, migrate_entry) {
+ list_for_each_entry_mutable(event, events, migrate_entry) {
list_del(&event->migrate_entry);
__perf_pmu_install_event(pmu, ctx, cpu, event);
}
@@ -14592,7 +14590,7 @@ perf_event_exit_event(struct perf_event *event,
static void perf_event_exit_task_context(struct task_struct *task, bool exit)
{
struct perf_event_context *ctx, *clone_ctx = NULL;
- struct perf_event *child_event, *next;
+ struct perf_event *child_event;
ctx = perf_pin_task_context(task);
if (!ctx)
@@ -14642,7 +14640,7 @@ static void perf_event_exit_task_context(struct task_struct *task, bool exit)
if (exit)
perf_event_task(task, ctx, 0);
- list_for_each_entry_safe(child_event, next, &ctx->event_list, event_entry)
+ list_for_each_entry_mutable(child_event, &ctx->event_list, event_entry)
perf_event_exit_event(child_event, ctx, exit ? task : NULL, false);
mutex_unlock(&ctx->mutex);
@@ -14675,13 +14673,13 @@ static void perf_event_exit_task_context(struct task_struct *task, bool exit)
*/
void perf_event_exit_task(struct task_struct *task)
{
- struct perf_event *event, *tmp;
+ struct perf_event *event;
WARN_ON_ONCE(task != current);
mutex_lock(&task->perf_event_mutex);
- list_for_each_entry_safe(event, tmp, &task->perf_event_list,
- owner_entry) {
+ list_for_each_entry_mutable(event, &task->perf_event_list,
+ owner_entry) {
list_del_init(&event->owner_entry);
/*
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 4084e926e284..61aa48e3b5a6 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -258,13 +258,13 @@ static void delayed_uprobe_delete(struct delayed_uprobe *du)
static void delayed_uprobe_remove(struct uprobe *uprobe, struct mm_struct *mm)
{
- struct list_head *pos, *q;
+ struct list_head *pos;
struct delayed_uprobe *du;
if (!uprobe && !mm)
return;
- list_for_each_safe(pos, q, &delayed_uprobe_list) {
+ list_for_each_mutable(pos, &delayed_uprobe_list) {
du = list_entry(pos, struct delayed_uprobe, list);
if (uprobe && du->uprobe != uprobe)
@@ -1562,13 +1562,13 @@ static void build_probe_list(struct inode *inode,
/* @vma contains reference counter, not the probed instruction. */
static int delayed_ref_ctr_inc(struct vm_area_struct *vma)
{
- struct list_head *pos, *q;
+ struct list_head *pos;
struct delayed_uprobe *du;
unsigned long vaddr;
int ret = 0, err = 0;
mutex_lock(&delayed_uprobe_lock);
- list_for_each_safe(pos, q, &delayed_uprobe_list) {
+ list_for_each_mutable(pos, &delayed_uprobe_list) {
du = list_entry(pos, struct delayed_uprobe, list);
if (du->mm != vma->vm_mm ||
@@ -1597,7 +1597,7 @@ static int delayed_ref_ctr_inc(struct vm_area_struct *vma)
int uprobe_mmap(struct vm_area_struct *vma)
{
struct list_head tmp_list;
- struct uprobe *uprobe, *u;
+ struct uprobe *uprobe;
struct inode *inode;
if (no_uprobe_events())
@@ -1622,7 +1622,7 @@ int uprobe_mmap(struct vm_area_struct *vma)
* removed. But in this case filter_chain() must return false, all
* consumers have gone away.
*/
- list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) {
+ list_for_each_entry_mutable(uprobe, &tmp_list, pending_list) {
if (!fatal_signal_pending(current) &&
filter_chain(uprobe, vma->vm_mm)) {
unsigned long vaddr = offset_to_vaddr(vma, uprobe->offset);
diff --git a/kernel/exit.c b/kernel/exit.c
index 1056422bc101..62ef6553253a 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -630,7 +630,7 @@ static struct task_struct *find_child_reaper(struct task_struct *father,
{
struct pid_namespace *pid_ns = task_active_pid_ns(father);
struct task_struct *reaper = pid_ns->child_reaper;
- struct task_struct *p, *n;
+ struct task_struct *p;
if (likely(reaper != father))
return reaper;
@@ -644,7 +644,7 @@ static struct task_struct *find_child_reaper(struct task_struct *father,
write_unlock_irq(&tasklist_lock);
- list_for_each_entry_safe(p, n, dead, ptrace_entry) {
+ list_for_each_entry_mutable(p, dead, ptrace_entry) {
list_del_init(&p->ptrace_entry);
release_task(p);
}
@@ -766,7 +766,7 @@ static void forget_original_parent(struct task_struct *father,
static void exit_notify(struct task_struct *tsk, int group_dead)
{
bool autoreap;
- struct task_struct *p, *n;
+ struct task_struct *p;
LIST_HEAD(dead);
write_lock_irq(&tasklist_lock);
@@ -800,7 +800,7 @@ static void exit_notify(struct task_struct *tsk, int group_dead)
wake_up_process(tsk->signal->group_exec_task);
write_unlock_irq(&tasklist_lock);
- list_for_each_entry_safe(p, n, &dead, ptrace_entry) {
+ list_for_each_entry_mutable(p, &dead, ptrace_entry) {
list_del_init(&p->ptrace_entry);
release_task(p);
}
diff --git a/kernel/fail_function.c b/kernel/fail_function.c
index 2eaf55005f49..357c810c4908 100644
--- a/kernel/fail_function.c
+++ b/kernel/fail_function.c
@@ -226,9 +226,9 @@ static void fei_attr_remove(struct fei_attr *attr)
static void fei_attr_remove_all(void)
{
- struct fei_attr *attr, *n;
+ struct fei_attr *attr;
- list_for_each_entry_safe(attr, n, &fei_attr_list, list) {
+ list_for_each_entry_mutable(attr, &fei_attr_list, list) {
fei_attr_remove(attr);
}
}
diff --git a/kernel/gcov/clang.c b/kernel/gcov/clang.c
index fd98ced0e51d..e9a86a04d793 100644
--- a/kernel/gcov/clang.c
+++ b/kernel/gcov/clang.c
@@ -347,9 +347,9 @@ struct gcov_info *gcov_info_dup(struct gcov_info *info)
*/
void gcov_info_free(struct gcov_info *info)
{
- struct gcov_fn_info *fn, *tmp;
+ struct gcov_fn_info *fn;
- list_for_each_entry_safe(fn, tmp, &info->functions, head) {
+ list_for_each_entry_mutable(fn, &info->functions, head) {
kvfree(fn->counters);
list_del(&fn->head);
kfree(fn);
diff --git a/kernel/irq_work.c b/kernel/irq_work.c
index f7e2dc2c30c6..ce454e12cd86 100644
--- a/kernel/irq_work.c
+++ b/kernel/irq_work.c
@@ -234,7 +234,7 @@ void irq_work_single(void *arg)
static void irq_work_run_list(struct llist_head *list)
{
- struct irq_work *work, *tmp;
+ struct irq_work *work;
struct llist_node *llnode;
/*
@@ -248,7 +248,7 @@ static void irq_work_run_list(struct llist_head *list)
return;
llnode = llist_del_all(list);
- llist_for_each_entry_safe(work, tmp, llnode, node.llist)
+ llist_for_each_entry_mutable(work, llnode, node.llist)
irq_work_single(work);
}
diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c
index dc770b9a6d05..d50d15d4709f 100644
--- a/kernel/kexec_core.c
+++ b/kernel/kexec_core.c
@@ -318,9 +318,9 @@ static void kimage_free_pages(struct page *page)
void kimage_free_page_list(struct list_head *list)
{
- struct page *page, *next;
+ struct page *page;
- list_for_each_entry_safe(page, next, list, lru) {
+ list_for_each_entry_mutable(page, list, lru) {
list_del(&page->lru);
kimage_free_pages(page);
}
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index bfc89083daa9..8e8fd6833d1c 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -228,12 +228,12 @@ static bool collect_one_slot(struct kprobe_insn_page *kip, int idx)
static int collect_garbage_slots(struct kprobe_insn_cache *c)
{
- struct kprobe_insn_page *kip, *next;
+ struct kprobe_insn_page *kip;
/* Ensure no-one is interrupted on the garbages */
synchronize_rcu();
- list_for_each_entry_safe(kip, next, &c->pages, list) {
+ list_for_each_entry_mutable(kip, &c->pages, list) {
int i;
if (kip->ngarbage == 0)
@@ -563,7 +563,7 @@ static void do_optimize_kprobes(void)
*/
static void do_unoptimize_kprobes(void)
{
- struct optimized_kprobe *op, *tmp;
+ struct optimized_kprobe *op;
lockdep_assert_held(&text_mutex);
/* See comment in do_optimize_kprobes() */
@@ -573,7 +573,7 @@ static void do_unoptimize_kprobes(void)
arch_unoptimize_kprobes(&unoptimizing_list, &freeing_list);
/* Loop on 'freeing_list' for disarming and removing from kprobe hash list */
- list_for_each_entry_safe(op, tmp, &freeing_list, list) {
+ list_for_each_entry_mutable(op, &freeing_list, list) {
/* Switching from detour code to origin */
op->kp.flags &= ~KPROBE_FLAG_OPTIMIZED;
/* Disarm probes if marked disabled and not gone */
@@ -594,9 +594,9 @@ static void do_unoptimize_kprobes(void)
/* Reclaim all kprobes on the 'freeing_list' */
static void do_free_cleaned_kprobes(void)
{
- struct optimized_kprobe *op, *tmp;
+ struct optimized_kprobe *op;
- list_for_each_entry_safe(op, tmp, &freeing_list, list) {
+ list_for_each_entry_mutable(op, &freeing_list, list) {
list_del_init(&op->list);
if (WARN_ON_ONCE(!kprobe_unused(&op->kp))) {
/*
@@ -2598,9 +2598,9 @@ static int __init populate_kprobe_blacklist(unsigned long *start,
/* Remove all symbols in given area from kprobe blacklist */
static void kprobe_remove_area_blacklist(unsigned long start, unsigned long end)
{
- struct kprobe_blacklist_entry *ent, *n;
+ struct kprobe_blacklist_entry *ent;
- list_for_each_entry_safe(ent, n, &kprobe_blacklist, list) {
+ list_for_each_entry_mutable(ent, &kprobe_blacklist, list) {
if (ent->start_addr < start || ent->start_addr >= end)
continue;
list_del(&ent->list);
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index 28d15ba58a26..6e433519bfff 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -788,9 +788,9 @@ void klp_free_patch_async(struct klp_patch *patch)
void klp_free_replaced_patches_async(struct klp_patch *new_patch)
{
- struct klp_patch *old_patch, *tmp_patch;
+ struct klp_patch *old_patch;
- klp_for_each_patch_safe(old_patch, tmp_patch) {
+ klp_for_each_patch_safe(old_patch) {
if (old_patch == new_patch)
return;
klp_free_patch_async(old_patch);
diff --git a/kernel/livepatch/core.h b/kernel/livepatch/core.h
index 38209c7361b6..274c8108062f 100644
--- a/kernel/livepatch/core.h
+++ b/kernel/livepatch/core.h
@@ -7,8 +7,8 @@
extern struct mutex klp_mutex;
extern struct list_head klp_patches;
-#define klp_for_each_patch_safe(patch, tmp_patch) \
- list_for_each_entry_safe(patch, tmp_patch, &klp_patches, list)
+#define klp_for_each_patch_safe(patch) \
+ list_for_each_entry_mutable(patch, &klp_patches, list)
#define klp_for_each_patch(patch) \
list_for_each_entry(patch, &klp_patches, list)
diff --git a/kernel/liveupdate/kho_block.c b/kernel/liveupdate/kho_block.c
index 0d2a342ef422..40e42a47751c 100644
--- a/kernel/liveupdate/kho_block.c
+++ b/kernel/liveupdate/kho_block.c
@@ -298,9 +298,9 @@ int kho_block_set_restore(struct kho_block_set *bs, u64 head_pa)
*/
void kho_block_set_destroy(struct kho_block_set *bs)
{
- struct kho_block *block, *tmp;
+ struct kho_block *block;
- list_for_each_entry_safe(block, tmp, &bs->blocks, list) {
+ list_for_each_entry_mutable(block, &bs->blocks, list) {
list_del(&block->list);
kho_block_free_ser(bs, block->ser);
kfree(block);
diff --git a/kernel/liveupdate/luo_flb.c b/kernel/liveupdate/luo_flb.c
index 5c27134ce7ba..cf5d577e25e4 100644
--- a/kernel/liveupdate/luo_flb.c
+++ b/kernel/liveupdate/luo_flb.c
@@ -359,13 +359,13 @@ static void luo_flb_unregister_one(struct liveupdate_file_handler *fh,
void luo_flb_unregister_all(struct liveupdate_file_handler *fh)
{
struct list_head *flb_list = &ACCESS_PRIVATE(fh, flb_list);
- struct luo_flb_link *iter, *tmp;
+ struct luo_flb_link *iter;
if (!liveupdate_enabled())
return;
lockdep_assert_held_write(&luo_register_rwlock);
- list_for_each_entry_safe(iter, tmp, flb_list, list)
+ list_for_each_entry_mutable(iter, flb_list, list)
luo_flb_unregister_one(fh, iter->flb);
}
diff --git a/kernel/locking/rwsem.c b/kernel/locking/rwsem.c
index b9c180ac1eee..8db6caf31e41 100644
--- a/kernel/locking/rwsem.c
+++ b/kernel/locking/rwsem.c
@@ -567,7 +567,7 @@ static void rwsem_mark_wake(struct rw_semaphore *sem,
atomic_long_add(adjustment, &sem->count);
/* 2nd pass */
- list_for_each_entry_safe(waiter, next, &wlist, list) {
+ list_for_each_entry_mutable(waiter, &wlist, list) {
struct task_struct *tsk;
tsk = waiter->task;
diff --git a/kernel/locking/test-ww_mutex.c b/kernel/locking/test-ww_mutex.c
index 838d631544ed..f290061a8b48 100644
--- a/kernel/locking/test-ww_mutex.c
+++ b/kernel/locking/test-ww_mutex.c
@@ -546,7 +546,7 @@ static void stress_reorder_work(struct work_struct *work)
} while (!time_after(jiffies, stress->timeout));
out:
- list_for_each_entry_safe(ll, ln, &locks, link)
+ list_for_each_entry_mutable(ll, &locks, link)
kfree(ll);
kfree(order);
}
diff --git a/kernel/module/main.c b/kernel/module/main.c
index 46dd8d25a605..471448804053 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -725,10 +725,10 @@ static int ref_module(struct module *a, struct module *b)
/* Clear the unload stuff of the module. */
static void module_unload_free(struct module *mod)
{
- struct module_use *use, *tmp;
+ struct module_use *use;
mutex_lock(&module_mutex);
- list_for_each_entry_safe(use, tmp, &mod->target_list, target_list) {
+ list_for_each_entry_mutable(use, &mod->target_list, target_list) {
struct module *i = use->target;
pr_debug("%s unusing %s\n", mod->name, i->name);
module_put(i);
@@ -3041,14 +3041,14 @@ struct mod_initfree {
static void do_free_init(struct work_struct *w)
{
- struct llist_node *pos, *n, *list;
+ struct llist_node *pos, *list;
struct mod_initfree *initfree;
list = llist_del_all(&init_free_list);
synchronize_rcu();
- llist_for_each_safe(pos, n, list) {
+ llist_for_each_mutable(pos, list) {
initfree = container_of(pos, struct mod_initfree, node);
execmem_free(initfree->init_text);
execmem_free(initfree->init_data);
@@ -3701,11 +3701,10 @@ static int idempotent_complete(struct idempotent *u, int ret)
const void *cookie = u->cookie;
int hash = hash_ptr(cookie, IDEM_HASH_BITS);
struct hlist_head *head = idem_hash + hash;
- struct hlist_node *next;
struct idempotent *pos;
spin_lock(&idem_lock);
- hlist_for_each_entry_safe(pos, next, head, entry) {
+ hlist_for_each_entry_mutable(pos, head, entry) {
if (pos->cookie != cookie)
continue;
hlist_del_init(&pos->entry);
diff --git a/kernel/padata.c b/kernel/padata.c
index 0d3ea1b68b1f..270f7b0eca0a 100644
--- a/kernel/padata.c
+++ b/kernel/padata.c
@@ -134,13 +134,13 @@ static void padata_work_free(struct padata_work *pw)
static void __init padata_works_free(struct list_head *works)
{
- struct padata_work *cur, *next;
+ struct padata_work *cur;
if (list_empty(works))
return;
spin_lock_bh(&padata_works_lock);
- list_for_each_entry_safe(cur, next, works, pw_list) {
+ list_for_each_entry_mutable(cur, works, pw_list) {
list_del(&cur->pw_list);
padata_work_free(cur);
}
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c
index d933b5b2c05d..c4802f2a2e35 100644
--- a/kernel/power/snapshot.c
+++ b/kernel/power/snapshot.c
@@ -610,9 +610,9 @@ struct mem_extent {
*/
static void free_mem_extents(struct list_head *list)
{
- struct mem_extent *ext, *aux;
+ struct mem_extent *ext;
- list_for_each_entry_safe(ext, aux, list, hook) {
+ list_for_each_entry_mutable(ext, list, hook) {
list_del(&ext->hook);
kfree(ext);
}
@@ -633,7 +633,7 @@ static int create_mem_extents(struct list_head *list, gfp_t gfp_mask)
for_each_populated_zone(zone) {
unsigned long zone_start, zone_end;
- struct mem_extent *ext, *cur, *aux;
+ struct mem_extent *ext, *cur;
zone_start = zone->zone_start_pfn;
zone_end = zone_end_pfn(zone);
@@ -665,7 +665,7 @@ static int create_mem_extents(struct list_head *list, gfp_t gfp_mask)
/* More merging may be possible */
cur = ext;
- list_for_each_entry_safe_continue(cur, aux, list, hook) {
+ list_for_each_entry_mutable_continue(cur, list, hook) {
if (zone_end < cur->start)
break;
if (zone_end < cur->end)
diff --git a/kernel/power/wakelock.c b/kernel/power/wakelock.c
index fd763da06a87..c8718aacfa5a 100644
--- a/kernel/power/wakelock.c
+++ b/kernel/power/wakelock.c
@@ -102,13 +102,13 @@ static inline void wakelocks_lru_most_recent(struct wakelock *wl)
static void __wakelocks_gc(struct work_struct *work)
{
- struct wakelock *wl, *aux;
+ struct wakelock *wl;
ktime_t now;
mutex_lock(&wakelocks_lock);
now = ktime_get();
- list_for_each_entry_safe_reverse(wl, aux, &wakelocks_lru_list, lru) {
+ list_for_each_entry_mutable_reverse(wl, &wakelocks_lru_list, lru) {
u64 idle_time_ns;
bool active;
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 2fe9a963c823..3f524c6bdf6e 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -3787,7 +3787,6 @@ static struct syscore printk_syscore = {
*/
static void printk_kthreads_check_locked(void)
{
- struct hlist_node *tmp;
struct console *con;
lockdep_assert_console_list_lock_held();
@@ -3805,7 +3804,7 @@ static void printk_kthreads_check_locked(void)
* are any nbcon consoles, they will set up their own
* kthread.
*/
- hlist_for_each_entry_safe(con, tmp, &console_list, node) {
+ hlist_for_each_entry_mutable(con, &console_list, node) {
if (con->flags & CON_NBCON)
continue;
@@ -3833,7 +3832,7 @@ static void printk_kthreads_check_locked(void)
if (printk_kthreads_running)
return;
- hlist_for_each_entry_safe(con, tmp, &console_list, node) {
+ hlist_for_each_entry_mutable(con, &console_list, node) {
if (!(con->flags & CON_NBCON))
continue;
@@ -4209,9 +4208,8 @@ void register_console(struct console *newcon)
if (bootcon_registered &&
((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV) &&
!keep_bootcon) {
- struct hlist_node *tmp;
- hlist_for_each_entry_safe(con, tmp, &console_list, node) {
+ hlist_for_each_entry_mutable(con, &console_list, node) {
if (con->flags & CON_BOOT)
unregister_console_locked(con);
}
@@ -4426,12 +4424,11 @@ void __init console_init(void)
*/
static int __init printk_late_init(void)
{
- struct hlist_node *tmp;
struct console *con;
int ret;
console_list_lock();
- hlist_for_each_entry_safe(con, tmp, &console_list, node) {
+ hlist_for_each_entry_mutable(con, &console_list, node) {
if (!(con->flags & CON_BOOT))
continue;
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index d041645d9d17..8032b653af83 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -619,9 +619,9 @@ static int ptrace_detach(struct task_struct *child, unsigned int data)
*/
void exit_ptrace(struct task_struct *tracer, struct list_head *dead)
{
- struct task_struct *p, *n;
+ struct task_struct *p;
- list_for_each_entry_safe(p, n, &tracer->ptraced, ptrace_entry) {
+ list_for_each_entry_mutable(p, &tracer->ptraced, ptrace_entry) {
if (unlikely(p->ptrace & PT_EXITKILL))
send_sig_info(SIGKILL, SEND_SIG_PRIV, p);
diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c
index 882a158ada7b..9eb0007411a9 100644
--- a/kernel/rcu/rcutorture.c
+++ b/kernel/rcu/rcutorture.c
@@ -537,11 +537,10 @@ static void
rcu_torture_pipe_update(struct rcu_torture *old_rp)
{
struct rcu_torture *rp;
- struct rcu_torture *rp1;
if (old_rp)
list_add(&old_rp->rtort_free, &rcu_torture_removed);
- list_for_each_entry_safe(rp, rp1, &rcu_torture_removed, rtort_free) {
+ list_for_each_entry_mutable(rp, &rcu_torture_removed, rtort_free) {
if (rcu_torture_pipe_update_one(rp)) {
list_del(&rp->rtort_free);
rcu_torture_free(rp);
diff --git a/kernel/rcu/tasks.h b/kernel/rcu/tasks.h
index f4da5fad70f5..e79380d93c85 100644
--- a/kernel/rcu/tasks.h
+++ b/kernel/rcu/tasks.h
@@ -1052,12 +1052,11 @@ static void rcu_tasks_postscan(struct list_head *hop)
for_each_possible_cpu(cpu) {
unsigned long j = jiffies + 1;
struct rcu_tasks_percpu *rtpcp = per_cpu_ptr(rcu_tasks.rtpcpu, cpu);
- struct task_struct *t;
- struct task_struct *t1;
+ struct task_struct *t, *t1;
struct list_head tmp;
raw_spin_lock_irq_rcu_node(rtpcp);
- list_for_each_entry_safe(t, t1, &rtpcp->rtp_exit_list, rcu_tasks_exit_list) {
+ list_for_each_entry_mutable(t, t1, &rtpcp->rtp_exit_list, rcu_tasks_exit_list) {
if (list_empty(&t->rcu_tasks_holdout_list))
rcu_tasks_pertask(t, hop);
@@ -1120,9 +1119,9 @@ static void check_holdout_task(struct task_struct *t,
static void check_all_holdout_tasks(struct list_head *hop,
bool needreport, bool *firstreport)
{
- struct task_struct *t, *t1;
+ struct task_struct *t;
- list_for_each_entry_safe(t, t1, hop, rcu_tasks_holdout_list) {
+ list_for_each_entry_mutable(t, hop, rcu_tasks_holdout_list) {
check_holdout_task(t, needreport, firstreport);
cond_resched();
}
diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c
index 03a43d3d2616..7c7792d62ac5 100644
--- a/kernel/rcu/tree.c
+++ b/kernel/rcu/tree.c
@@ -1664,7 +1664,7 @@ static void rcu_sr_normal_complete(struct llist_node *node)
static void rcu_sr_normal_gp_cleanup_work(struct work_struct *work)
{
- struct llist_node *done, *rcu, *next, *head;
+ struct llist_node *done, *rcu, *head;
/*
* This work execution can potentially execute
@@ -1694,7 +1694,7 @@ static void rcu_sr_normal_gp_cleanup_work(struct work_struct *work)
* nodes is removed, in next round of cleanup
* work execution.
*/
- llist_for_each_safe(rcu, next, head) {
+ llist_for_each_mutable(rcu, head) {
if (!rcu_sr_is_wait_head(rcu)) {
rcu_sr_normal_complete(rcu);
continue;
@@ -1726,7 +1726,7 @@ static void rcu_sr_normal_gp_cleanup(void)
/*
* Process (a) and (d) cases. See an illustration.
*/
- llist_for_each_safe(rcu, next, wait_tail->next) {
+ llist_for_each_mutable(rcu, next, wait_tail->next) {
if (rcu_sr_is_wait_head(rcu))
break;
diff --git a/kernel/resource.c b/kernel/resource.c
index e60539a55541..19b89b09e291 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -1951,9 +1951,9 @@ EXPORT_SYMBOL(resource_list_create_entry);
void resource_list_free(struct list_head *head)
{
- struct resource_entry *entry, *tmp;
+ struct resource_entry *entry;
- list_for_each_entry_safe(entry, tmp, head, node)
+ list_for_each_entry_mutable(entry, head, node)
resource_list_destroy_entry(entry);
}
EXPORT_SYMBOL(resource_list_free);
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index e97e98c33be5..873e26f2a032 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -3892,7 +3892,7 @@ void sched_ttwu_pending(void *arg)
{
struct llist_node *llist = arg;
struct rq *rq = this_rq();
- struct task_struct *p, *t;
+ struct task_struct *p;
struct rq_flags rf;
if (!llist)
@@ -3901,7 +3901,7 @@ void sched_ttwu_pending(void *arg)
rq_lock_irqsave(rq, &rf);
update_rq_clock(rq);
- llist_for_each_entry_safe(p, t, llist, wake_entry.llist) {
+ llist_for_each_entry_mutable(p, llist, wake_entry.llist) {
if (WARN_ON_ONCE(p->on_cpu))
smp_cond_load_acquire(&p->on_cpu, !VAL);
diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c
index 0db6fa2daea3..2119f2629fd1 100644
--- a/kernel/sched/ext.c
+++ b/kernel/sched/ext.c
@@ -4121,7 +4121,7 @@ static void process_ddsp_deferred_locals(struct rq *rq)
* Now that @rq can be unlocked, execute the deferred enqueueing of
* tasks directly dispatched to the local DSQs of other CPUs. See
* direct_dispatch(). Keep popping from the head instead of using
- * list_for_each_entry_safe() as dispatch_local_dsq() may unlock @rq
+ * list_for_each_entry_mutable() as dispatch_local_dsq() may unlock @rq
* temporarily.
*/
while ((p = list_first_entry_or_null(&rq->scx.ddsp_deferred_locals,
@@ -4186,7 +4186,7 @@ static u32 reenq_local(struct scx_sched *sch, struct rq *rq, u64 reenq_flags)
{
LIST_HEAD(tasks);
u32 nr_enqueued = 0;
- struct task_struct *p, *n;
+ struct task_struct *p;
lockdep_assert_rq_held(rq);
@@ -4200,8 +4200,8 @@ static u32 reenq_local(struct scx_sched *sch, struct rq *rq, u64 reenq_flags)
* @rq->scx.local_dsq. Move all candidate tasks off to a private list
* first to avoid processing the same tasks repeatedly.
*/
- list_for_each_entry_safe(p, n, &rq->scx.local_dsq.list,
- scx.dsq_list.node) {
+ list_for_each_entry_mutable(p, &rq->scx.local_dsq.list,
+ scx.dsq_list.node) {
struct scx_sched *task_sch = scx_task_sched(p);
u32 reason;
@@ -4234,7 +4234,7 @@ static u32 reenq_local(struct scx_sched *sch, struct rq *rq, u64 reenq_flags)
list_add_tail(&p->scx.dsq_list.node, &tasks);
}
- list_for_each_entry_safe(p, n, &tasks, scx.dsq_list.node) {
+ list_for_each_entry_mutable(p, &tasks, scx.dsq_list.node) {
list_del_init(&p->scx.dsq_list.node);
do_enqueue_task(rq, p, SCX_ENQ_REENQ, -1);
@@ -4786,9 +4786,9 @@ static void free_dsq_rcufn(struct rcu_head *rcu)
static void free_dsq_irq_workfn(struct irq_work *irq_work)
{
struct llist_node *to_free = llist_del_all(&dsqs_to_free);
- struct scx_dispatch_q *dsq, *tmp_dsq;
+ struct scx_dispatch_q *dsq;
- llist_for_each_entry_safe(dsq, tmp_dsq, to_free, free_node)
+ llist_for_each_entry_mutable(dsq, to_free, free_node)
call_rcu(&dsq->rcu, free_dsq_rcufn);
}
@@ -5684,7 +5684,7 @@ static void scx_bypass(struct scx_sched *sch, bool bypass)
*/
for_each_possible_cpu(cpu) {
struct rq *rq = cpu_rq(cpu);
- struct task_struct *p, *n;
+ struct task_struct *p;
raw_spin_rq_lock(rq);
raw_spin_lock(&scx_sched_lock);
@@ -5711,14 +5711,14 @@ static void scx_bypass(struct scx_sched *sch, bool bypass)
}
/*
- * The use of list_for_each_entry_safe_reverse() is required
+ * The use of list_for_each_entry_mutable_reverse() is required
* because each task is going to be removed from and added back
* to the runnable_list during iteration. Because they're added
* to the tail of the list, safe reverse iteration can still
* visit all nodes.
*/
- list_for_each_entry_safe_reverse(p, n, &rq->scx.runnable_list,
- scx.runnable_node) {
+ list_for_each_entry_mutable_reverse(p, &rq->scx.runnable_list,
+ scx.runnable_node) {
if (!scx_is_descendant(scx_task_sched(p), sch))
continue;
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index d78467ec6ee1..0f21a168fd9a 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -408,9 +408,9 @@ static inline void assert_list_leaf_cfs_rq(struct rq *rq)
}
/* Iterate through all leaf cfs_rq's on a runqueue */
-#define for_each_leaf_cfs_rq_safe(rq, cfs_rq, pos) \
- list_for_each_entry_safe(cfs_rq, pos, &rq->leaf_cfs_rq_list, \
- leaf_cfs_rq_list)
+#define for_each_leaf_cfs_rq_safe(rq, cfs_rq) \
+ list_for_each_entry_mutable(cfs_rq, &(rq)->leaf_cfs_rq_list, \
+ leaf_cfs_rq_list)
/* Do the two (enqueued) entities belong to the same group ? */
static inline struct cfs_rq *
@@ -494,8 +494,8 @@ static inline void assert_list_leaf_cfs_rq(struct rq *rq)
{
}
-#define for_each_leaf_cfs_rq_safe(rq, cfs_rq, pos) \
- for (cfs_rq = &rq->cfs, pos = NULL; cfs_rq; cfs_rq = pos)
+#define for_each_leaf_cfs_rq_safe(rq, cfs_rq) \
+ for (cfs_rq = &(rq)->cfs; cfs_rq; cfs_rq = NULL)
static inline struct sched_entity *parent_entity(struct sched_entity *se)
{
@@ -6724,7 +6724,7 @@ static int tg_unthrottle_up(struct task_group *tg, void *data)
{
struct rq *rq = data;
struct cfs_rq *cfs_rq = tg_cfs_rq(tg, cpu_of(rq));
- struct task_struct *p, *tmp;
+ struct task_struct *p;
LIST_HEAD(throttled_tasks);
/*
@@ -6765,7 +6765,7 @@ static int tg_unthrottle_up(struct task_group *tg, void *data)
list_splice_init(&cfs_rq->throttled_limbo_list, &throttled_tasks);
/* Re-enqueue the tasks that have been throttled at this level. */
- list_for_each_entry_safe(p, tmp, &throttled_tasks, throttle_node) {
+ list_for_each_entry_mutable(p, &throttled_tasks, throttle_node) {
/*
* Back to being throttled! Break out and put the remaining
* tasks back onto the limbo_list to prevent running them
@@ -6966,7 +6966,7 @@ void unthrottle_cfs_rq(struct cfs_rq *cfs_rq)
static void __cfsb_csd_unthrottle(void *arg)
{
- struct cfs_rq *cursor, *tmp;
+ struct cfs_rq *cursor;
struct rq *rq = arg;
guard(rq_lock)(rq);
@@ -6988,8 +6988,8 @@ static void __cfsb_csd_unthrottle(void *arg)
*/
guard(rcu)();
- list_for_each_entry_safe(cursor, tmp, &rq->cfsb_csd_list,
- throttled_csd_list) {
+ list_for_each_entry_mutable(cursor, &rq->cfsb_csd_list,
+ throttled_csd_list) {
list_del_init(&cursor->throttled_csd_list);
if (cfs_rq_throttled(cursor))
@@ -11118,14 +11118,14 @@ static bool __update_blocked_others(struct rq *rq, bool *done)
static bool __update_blocked_fair(struct rq *rq, bool *done)
{
- struct cfs_rq *cfs_rq, *pos;
+ struct cfs_rq *cfs_rq;
bool decayed = false;
/*
* Iterates the task_group tree in a bottom up fashion, see
* list_add_leaf_cfs_rq() for details.
*/
- for_each_leaf_cfs_rq_safe(rq, cfs_rq, pos) {
+ for_each_leaf_cfs_rq_safe(rq, cfs_rq) {
struct sched_entity *se;
if (update_cfs_rq_load_avg(cfs_rq_clock_pelt(cfs_rq), cfs_rq)) {
@@ -15401,10 +15401,10 @@ DEFINE_SCHED_CLASS(fair) = {
void print_cfs_stats(struct seq_file *m, int cpu)
{
- struct cfs_rq *cfs_rq, *pos;
+ struct cfs_rq *cfs_rq;
rcu_read_lock();
- for_each_leaf_cfs_rq_safe(cpu_rq(cpu), cfs_rq, pos)
+ for_each_leaf_cfs_rq_safe(cpu_rq(cpu), cfs_rq)
print_cfs_rq(m, cpu, cfs_rq);
rcu_read_unlock();
}
diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c
index 622e2e01974c..062887738bff 100644
--- a/kernel/sched/topology.c
+++ b/kernel/sched/topology.c
@@ -1753,7 +1753,7 @@ static inline void asym_cpu_capacity_update_data(int cpu)
*/
static void asym_cpu_capacity_scan(void)
{
- struct asym_cap_data *entry, *next;
+ struct asym_cap_data *entry;
int cpu;
list_for_each_entry(entry, &asym_cap_list, link)
@@ -1762,7 +1762,7 @@ static void asym_cpu_capacity_scan(void)
for_each_cpu_and(cpu, cpu_possible_mask, housekeeping_cpumask(HK_TYPE_DOMAIN))
asym_cpu_capacity_update_data(cpu);
- list_for_each_entry_safe(entry, next, &asym_cap_list, link) {
+ list_for_each_entry_mutable(entry, &asym_cap_list, link) {
if (cpumask_empty(cpu_capacity_span(entry))) {
list_del_rcu(&entry->link);
call_rcu(&entry->rcu, free_asym_cap_entry);
diff --git a/kernel/sched/wait.c b/kernel/sched/wait.c
index 20f27e2cf7ae..3411f9ac2073 100644
--- a/kernel/sched/wait.c
+++ b/kernel/sched/wait.c
@@ -92,7 +92,7 @@ EXPORT_SYMBOL(remove_wait_queue);
static int __wake_up_common(struct wait_queue_head *wq_head, unsigned int mode,
int nr_exclusive, int wake_flags, void *key)
{
- wait_queue_entry_t *curr, *next;
+ wait_queue_entry_t *curr;
lockdep_assert_held(&wq_head->lock);
@@ -101,7 +101,7 @@ static int __wake_up_common(struct wait_queue_head *wq_head, unsigned int mode,
if (&curr->entry == &wq_head->head)
return nr_exclusive;
- list_for_each_entry_safe_from(curr, next, &wq_head->head, entry) {
+ list_for_each_entry_mutable_from(curr, &wq_head->head, entry) {
unsigned flags = curr->flags;
int ret;
diff --git a/kernel/seccomp.c b/kernel/seccomp.c
index 066909393c38..7212e47703f9 100644
--- a/kernel/seccomp.c
+++ b/kernel/seccomp.c
@@ -1168,7 +1168,7 @@ static int seccomp_do_user_notification(int this_syscall,
u32 flags = 0;
long ret = 0;
struct seccomp_knotif n = {};
- struct seccomp_kaddfd *addfd, *tmp;
+ struct seccomp_kaddfd *addfd;
mutex_lock(&match->notify_lock);
err = -ENOSYS;
@@ -1225,7 +1225,7 @@ static int seccomp_do_user_notification(int this_syscall,
interrupted:
/* If there were any pending addfd calls, clear them out */
- list_for_each_entry_safe(addfd, tmp, &n.addfd, list) {
+ list_for_each_entry_mutable(addfd, &n.addfd, list) {
/* The process went away before we got a chance to handle it */
addfd->ret = -ESRCH;
list_del_init(&addfd->list);
diff --git a/kernel/signal.c b/kernel/signal.c
index 9c2b32c4d755..072231bebf52 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -748,7 +748,7 @@ static void sigqueue_free_ignored(struct task_struct *tsk, struct sigqueue *q)
/* Remove signals in mask from the pending set and queue. */
static void flush_sigqueue_mask(struct task_struct *p, sigset_t *mask, struct sigpending *s)
{
- struct sigqueue *q, *n;
+ struct sigqueue *q;
sigset_t m;
lockdep_assert_held(&p->sighand->siglock);
@@ -758,7 +758,7 @@ static void flush_sigqueue_mask(struct task_struct *p, sigset_t *mask, struct si
return;
sigandnsets(&s->signal, &s->signal, mask);
- list_for_each_entry_safe(q, n, &s->list, list) {
+ list_for_each_entry_mutable(q, &s->list, list) {
if (sigismember(mask, q->info.si_signo)) {
list_del_init(&q->list);
sigqueue_free_ignored(p, q);
@@ -1899,12 +1899,12 @@ EXPORT_SYMBOL(kill_pid);
static void __flush_itimer_signals(struct sigpending *pending)
{
sigset_t signal, retain;
- struct sigqueue *q, *n;
+ struct sigqueue *q;
signal = pending->signal;
sigemptyset(&retain);
- list_for_each_entry_safe(q, n, &pending->list, list) {
+ list_for_each_entry_mutable(q, &pending->list, list) {
int sig = q->info.si_signo;
if (likely(q->info.si_code != SI_TIMER)) {
@@ -2101,7 +2101,6 @@ static inline void posixtimer_sig_ignore(struct task_struct *tsk, struct sigqueu
static void posixtimer_sig_unignore(struct task_struct *tsk, int sig)
{
struct hlist_head *head = &tsk->signal->ignored_posix_timers;
- struct hlist_node *tmp;
struct k_itimer *tmr;
if (likely(hlist_empty(head)))
@@ -2114,7 +2113,7 @@ static void posixtimer_sig_unignore(struct task_struct *tsk, int sig)
* rearmed or not. This cannot be decided here w/o dropping sighand
* lock and creating a loop retry horror show.
*/
- hlist_for_each_entry_safe(tmr, tmp , head, ignored_list) {
+ hlist_for_each_entry_mutable(tmr, head, ignored_list) {
struct task_struct *target;
/*
diff --git a/kernel/smp.c b/kernel/smp.c
index a0bb56bd8dda..305187e50b58 100644
--- a/kernel/smp.c
+++ b/kernel/smp.c
@@ -544,7 +544,7 @@ static void __flush_smp_call_function_queue(bool warn_cpu_offline)
* First; run all SYNC callbacks, people are waiting for us.
*/
prev = NULL;
- llist_for_each_entry_safe(csd, csd_next, entry, node.llist) {
+ llist_for_each_entry_mutable(csd, csd_next, entry, node.llist) {
/* Do we wait until *after* callback? */
if (CSD_TYPE(csd) == CSD_TYPE_SYNC) {
smp_call_func_t func = csd->func;
@@ -572,7 +572,7 @@ static void __flush_smp_call_function_queue(bool warn_cpu_offline)
* Second; run all !SYNC callbacks.
*/
prev = NULL;
- llist_for_each_entry_safe(csd, csd_next, entry, node.llist) {
+ llist_for_each_entry_mutable(csd, csd_next, entry, node.llist) {
int type = CSD_TYPE(csd);
if (type != CSD_TYPE_TTWU) {
diff --git a/kernel/taskstats.c b/kernel/taskstats.c
index 2cd0172d0516..08932d4776d1 100644
--- a/kernel/taskstats.c
+++ b/kernel/taskstats.c
@@ -111,7 +111,7 @@ static void send_cpu_listeners(struct sk_buff *skb,
struct listener_list *listeners)
{
struct genlmsghdr *genlhdr = nlmsg_data(nlmsg_hdr(skb));
- struct listener *s, *tmp;
+ struct listener *s;
struct sk_buff *skb_next, *skb_cur = skb;
void *reply = genlmsg_data(genlhdr);
int delcount = 0;
@@ -145,7 +145,7 @@ static void send_cpu_listeners(struct sk_buff *skb,
/* Delete invalidated entries */
down_write(&listeners->sem);
- list_for_each_entry_safe(s, tmp, &listeners->list, list) {
+ list_for_each_entry_mutable(s, &listeners->list, list) {
if (!s->valid) {
list_del(&s->list);
kfree(s);
@@ -299,7 +299,7 @@ static void fill_tgid_exit(struct task_struct *tsk)
static int add_del_listener(pid_t pid, const struct cpumask *mask, int isadd)
{
struct listener_list *listeners;
- struct listener *s, *tmp, *s2;
+ struct listener *s, *s2;
unsigned int cpu;
int ret = 0;
@@ -343,7 +343,7 @@ static int add_del_listener(pid_t pid, const struct cpumask *mask, int isadd)
for_each_cpu(cpu, mask) {
listeners = &per_cpu(listener_array, cpu);
down_write(&listeners->sem);
- list_for_each_entry_safe(s, tmp, &listeners->list, list) {
+ list_for_each_entry_mutable(s, &listeners->list, list) {
if (s->pid == pid) {
list_del(&s->list);
kfree(s);
diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c
index 0014d163f989..a13eeab8d83f 100644
--- a/kernel/time/clockevents.c
+++ b/kernel/time/clockevents.c
@@ -678,7 +678,7 @@ void clockevents_resume(void)
*/
void tick_offline_cpu(unsigned int cpu)
{
- struct clock_event_device *dev, *tmp;
+ struct clock_event_device *dev;
raw_spin_lock(&clockevents_lock);
@@ -689,13 +689,13 @@ void tick_offline_cpu(unsigned int cpu)
* Unregister the clock event devices which were
* released above.
*/
- list_for_each_entry_safe(dev, tmp, &clockevents_released, list)
+ list_for_each_entry_mutable(dev, &clockevents_released, list)
list_del(&dev->list);
/*
* Now check whether the CPU has left unused per cpu devices
*/
- list_for_each_entry_safe(dev, tmp, &clockevent_devices, list) {
+ list_for_each_entry_mutable(dev, &clockevent_devices, list) {
if (cpumask_test_cpu(cpu, dev->cpumask) &&
cpumask_weight(dev->cpumask) == 1 &&
!tick_is_broadcast_device(dev)) {
diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c
index e48c4d379a7c..ac857d7ce4bb 100644
--- a/kernel/time/clocksource.c
+++ b/kernel/time/clocksource.c
@@ -776,12 +776,12 @@ static void clocksource_dequeue_watchdog(struct clocksource *cs)
static int __clocksource_watchdog_kthread(void)
{
- struct clocksource *cs, *tmp;
+ struct clocksource *cs;
unsigned long flags;
int select = 0;
spin_lock_irqsave(&watchdog_lock, flags);
- list_for_each_entry_safe(cs, tmp, &watchdog_list, wd_list) {
+ list_for_each_entry_mutable(cs, &watchdog_list, wd_list) {
if (cs->flags & CLOCK_SOURCE_UNSTABLE) {
list_del_init(&cs->wd_list);
clocksource_change_rating(cs, 0);
diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c
index 5e633d8750d1..1f2c5533d598 100644
--- a/kernel/time/posix-cpu-timers.c
+++ b/kernel/time/posix-cpu-timers.c
@@ -1296,7 +1296,7 @@ static inline bool posix_cpu_timers_enable_work(struct task_struct *tsk,
static void handle_posix_cpu_timers(struct task_struct *tsk)
{
- struct k_itimer *timer, *next;
+ struct k_itimer *timer;
unsigned long flags, start;
LIST_HEAD(firing);
@@ -1369,7 +1369,7 @@ static void handle_posix_cpu_timers(struct task_struct *tsk)
* each timer's lock before clearing its firing flag, so no
* timer call will interfere.
*/
- list_for_each_entry_safe(timer, next, &firing, it.cpu.elist) {
+ list_for_each_entry_mutable(timer, &firing, it.cpu.elist) {
bool cpu_firing;
/*
diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c
index 436ba794cc0b..d89307c4adb0 100644
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -1085,7 +1085,6 @@ SYSCALL_DEFINE1(timer_delete, timer_t, timer_id)
void exit_itimers(struct task_struct *tsk)
{
struct hlist_head timers;
- struct hlist_node *next;
struct k_itimer *timer;
/* Clear restore mode for exec() */
@@ -1099,7 +1098,7 @@ void exit_itimers(struct task_struct *tsk)
hlist_move_list(&tsk->signal->posix_timers, &timers);
/* The timers are not longer accessible via tsk::signal */
- hlist_for_each_entry_safe(timer, next, &timers, list) {
+ hlist_for_each_entry_mutable(timer, &timers, list) {
scoped_guard (spinlock_irq, &timer->it_lock)
posix_timer_delete(timer);
posix_timer_unhash_and_free(timer);
diff --git a/kernel/torture.c b/kernel/torture.c
index 77cb3589b19f..047dcde729dd 100644
--- a/kernel/torture.c
+++ b/kernel/torture.c
@@ -510,10 +510,9 @@ EXPORT_SYMBOL_GPL(torture_shuffle_task_register);
static void torture_shuffle_task_unregister_all(void)
{
struct shuffle_task *stp;
- struct shuffle_task *p;
mutex_lock(&shuffle_task_mutex);
- list_for_each_entry_safe(stp, p, &shuffle_task_list, st_l) {
+ list_for_each_entry_mutable(stp, &shuffle_task_list, st_l) {
list_del(&stp->st_l);
kfree(stp);
}
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index 82f8feea6931..7f6d8b287adb 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -2276,7 +2276,7 @@ subsys_initcall(send_signal_irq_work_init);
static int bpf_event_notify(struct notifier_block *nb, unsigned long op,
void *module)
{
- struct bpf_trace_module *btm, *tmp;
+ struct bpf_trace_module *btm;
struct module *mod = module;
int ret = 0;
@@ -2297,7 +2297,7 @@ static int bpf_event_notify(struct notifier_block *nb, unsigned long op,
}
break;
case MODULE_STATE_GOING:
- list_for_each_entry_safe(btm, tmp, &bpf_trace_modules, list) {
+ list_for_each_entry_mutable(btm, &bpf_trace_modules, list) {
if (btm->module == module) {
list_del(&btm->list);
kfree(btm);
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index f93e34dd2328..422665c687ff 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -1252,7 +1252,6 @@ void ftrace_hash_remove(struct ftrace_hash *hash)
{
struct ftrace_func_entry *entry;
struct hlist_head *hhd;
- struct hlist_node *tn;
int size;
int i;
@@ -1261,7 +1260,7 @@ void ftrace_hash_remove(struct ftrace_hash *hash)
size = 1 << hash->size_bits;
for (i = 0; i < size; i++) {
hhd = &hash->buckets[i];
- hlist_for_each_entry_safe(entry, tn, hhd, hlist)
+ hlist_for_each_entry_mutable(entry, hhd, hlist)
remove_hash_entry(hash, entry);
}
FTRACE_WARN_ON(hash->count);
@@ -1270,7 +1269,6 @@ void ftrace_hash_remove(struct ftrace_hash *hash)
static void ftrace_hash_clear(struct ftrace_hash *hash)
{
struct hlist_head *hhd;
- struct hlist_node *tn;
struct ftrace_func_entry *entry;
int size = 1 << hash->size_bits;
int i;
@@ -1280,7 +1278,7 @@ static void ftrace_hash_clear(struct ftrace_hash *hash)
for (i = 0; i < size; i++) {
hhd = &hash->buckets[i];
- hlist_for_each_entry_safe(entry, tn, hhd, hlist)
+ hlist_for_each_entry_mutable(entry, hhd, hlist)
free_hash_entry(hash, entry);
}
FTRACE_WARN_ON(hash->count);
@@ -1296,14 +1294,14 @@ static void free_ftrace_mod(struct ftrace_mod_load *ftrace_mod)
static void clear_ftrace_mod_list(struct list_head *head)
{
- struct ftrace_mod_load *p, *n;
+ struct ftrace_mod_load *p;
/* stack tracer isn't supported yet */
if (!head)
return;
mutex_lock(&ftrace_lock);
- list_for_each_entry_safe(p, n, head, list)
+ list_for_each_entry_mutable(p, head, list)
free_ftrace_mod(p);
mutex_unlock(&ftrace_lock);
}
@@ -1451,7 +1449,6 @@ static struct ftrace_hash *__move_hash(struct ftrace_hash *src, int size)
struct ftrace_func_entry *entry;
struct ftrace_hash *new_hash;
struct hlist_head *hhd;
- struct hlist_node *tn;
int bits = 0;
int i;
@@ -1474,7 +1471,7 @@ static struct ftrace_hash *__move_hash(struct ftrace_hash *src, int size)
size = 1 << src->size_bits;
for (i = 0; i < size; i++) {
hhd = &src->buckets[i];
- hlist_for_each_entry_safe(entry, tn, hhd, hlist) {
+ hlist_for_each_entry_mutable(entry, hhd, hlist) {
remove_hash_entry(src, entry);
add_ftrace_hash_entry(new_hash, entry);
}
@@ -3327,7 +3324,6 @@ static int append_hash(struct ftrace_hash **hash, struct ftrace_hash *new_hash,
static void remove_hash(struct ftrace_hash *hash, struct ftrace_hash *notrace_hash)
{
struct ftrace_func_entry *entry;
- struct hlist_node *tmp;
int size;
int i;
@@ -3337,7 +3333,7 @@ static void remove_hash(struct ftrace_hash *hash, struct ftrace_hash *notrace_ha
size = 1 << hash->size_bits;
for (i = 0; i < size; i++) {
- hlist_for_each_entry_safe(entry, tmp, &hash->buckets[i], hlist) {
+ hlist_for_each_entry_mutable(entry, &hash->buckets[i], hlist) {
if (!__ftrace_lookup_ip(notrace_hash, entry->ip))
continue;
remove_hash_entry(hash, entry);
@@ -5084,7 +5080,7 @@ static int ftrace_hash_move_and_update_ops(struct ftrace_ops *ops,
static int cache_mod(struct trace_array *tr,
const char *func, char *module, int enable)
{
- struct ftrace_mod_load *ftrace_mod, *n;
+ struct ftrace_mod_load *ftrace_mod;
struct list_head *head = enable ? &tr->mod_trace : &tr->mod_notrace;
guard(mutex)(&ftrace_lock);
@@ -5096,7 +5092,7 @@ static int cache_mod(struct trace_array *tr,
func++;
/* Look to remove this hash */
- list_for_each_entry_safe(ftrace_mod, n, head, list) {
+ list_for_each_entry_mutable(ftrace_mod, head, list) {
if (strcmp(ftrace_mod->module, module) != 0)
continue;
@@ -5124,7 +5120,7 @@ static int cache_mod(struct trace_array *tr,
static void process_mod_list(struct list_head *head, struct ftrace_ops *ops,
char *mod, bool enable)
{
- struct ftrace_mod_load *ftrace_mod, *n;
+ struct ftrace_mod_load *ftrace_mod;
struct ftrace_hash **orig_hash, *new_hash;
LIST_HEAD(process_mods);
char *func;
@@ -5143,7 +5139,7 @@ static void process_mod_list(struct list_head *head, struct ftrace_ops *ops,
mutex_lock(&ftrace_lock);
- list_for_each_entry_safe(ftrace_mod, n, head, list) {
+ list_for_each_entry_mutable(ftrace_mod, head, list) {
if (strcmp(ftrace_mod->module, mod) != 0)
continue;
@@ -5165,7 +5161,7 @@ static void process_mod_list(struct list_head *head, struct ftrace_ops *ops,
mutex_unlock(&ftrace_lock);
- list_for_each_entry_safe(ftrace_mod, n, &process_mods, list) {
+ list_for_each_entry_mutable(ftrace_mod, &process_mods, list) {
func = ftrace_mod->func;
@@ -5616,7 +5612,6 @@ unregister_ftrace_function_probe_func(char *glob, struct trace_array *tr,
struct ftrace_hash **orig_hash;
struct ftrace_hash *old_hash;
struct ftrace_hash *hash = NULL;
- struct hlist_node *tmp;
struct hlist_head hhd;
char str[KSYM_SYMBOL_LEN];
int count = 0;
@@ -5677,7 +5672,7 @@ unregister_ftrace_function_probe_func(char *glob, struct trace_array *tr,
size = 1 << hash->size_bits;
for (i = 0; i < size; i++) {
- hlist_for_each_entry_safe(entry, tmp, &hash->buckets[i], hlist) {
+ hlist_for_each_entry_mutable(entry, &hash->buckets[i], hlist) {
if (func_g.search) {
kallsyms_lookup(entry->ip, NULL, NULL,
@@ -5715,7 +5710,7 @@ unregister_ftrace_function_probe_func(char *glob, struct trace_array *tr,
&old_hash_ops);
synchronize_rcu();
- hlist_for_each_entry_safe(entry, tmp, &hhd, hlist) {
+ hlist_for_each_entry_mutable(entry, &hhd, hlist) {
hlist_del(&entry->hlist);
if (probe_ops->free)
probe_ops->free(probe_ops, tr, entry->ip, probe->data);
@@ -5738,9 +5733,9 @@ unregister_ftrace_function_probe_func(char *glob, struct trace_array *tr,
void clear_ftrace_function_probes(struct trace_array *tr)
{
- struct ftrace_func_probe *probe, *n;
+ struct ftrace_func_probe *probe;
- list_for_each_entry_safe(probe, n, &tr->func_probes, list)
+ list_for_each_entry_mutable(probe, &tr->func_probes, list)
unregister_ftrace_function_probe_func(NULL, tr, probe->probe_ops);
}
@@ -5771,11 +5766,11 @@ __init int register_ftrace_command(struct ftrace_func_command *cmd)
*/
__init int unregister_ftrace_command(struct ftrace_func_command *cmd)
{
- struct ftrace_func_command *p, *n;
+ struct ftrace_func_command *p;
guard(mutex)(&ftrace_cmd_mutex);
- list_for_each_entry_safe(p, n, &ftrace_commands, list) {
+ list_for_each_entry_mutable(p, &ftrace_commands, list) {
if (strcmp(cmd->name, p->name) == 0) {
list_del_init(&p->list);
return 0;
@@ -7876,10 +7871,9 @@ static void ftrace_free_mod_map(struct rcu_head *rcu)
{
struct ftrace_mod_map *mod_map = container_of(rcu, struct ftrace_mod_map, rcu);
struct ftrace_mod_func *mod_func;
- struct ftrace_mod_func *n;
/* All the contents of mod_map are now not visible to readers */
- list_for_each_entry_safe(mod_func, n, &mod_map->funcs, list) {
+ list_for_each_entry_mutable(mod_func, &mod_map->funcs, list) {
kfree(mod_func->name);
list_del(&mod_func->list);
kfree(mod_func);
@@ -7891,7 +7885,6 @@ static void ftrace_free_mod_map(struct rcu_head *rcu)
void ftrace_release_mod(struct module *mod)
{
struct ftrace_mod_map *mod_map;
- struct ftrace_mod_map *n;
struct dyn_ftrace *rec;
struct ftrace_page **last_pg;
struct ftrace_page *tmp_page = NULL;
@@ -7903,7 +7896,7 @@ void ftrace_release_mod(struct module *mod)
* To avoid the UAF problem after the module is unloaded, the
* 'mod_map' resource needs to be released unconditionally.
*/
- list_for_each_entry_safe(mod_map, n, &ftrace_mod_maps, list) {
+ list_for_each_entry_mutable(mod_map, &ftrace_mod_maps, list) {
if (mod_map->mod == mod) {
list_del_rcu(&mod_map->list);
call_rcu(&mod_map->rcu, ftrace_free_mod_map);
@@ -8290,7 +8283,7 @@ void ftrace_free_mem(struct module *mod, void *start_ptr, void *end_ptr)
struct dyn_ftrace *rec;
struct dyn_ftrace key;
struct ftrace_mod_map *mod_map = NULL;
- struct ftrace_init_func *func, *func_next;
+ struct ftrace_init_func *func;
LIST_HEAD(clear_hash);
key.ip = start;
@@ -8341,7 +8334,7 @@ void ftrace_free_mem(struct module *mod, void *start_ptr, void *end_ptr)
}
mutex_unlock(&ftrace_lock);
- list_for_each_entry_safe(func, func_next, &clear_hash, list) {
+ list_for_each_entry_mutable(func, &clear_hash, list) {
clear_func_from_hashes(func);
kfree(func);
}
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 56a328e94395..24b2deb1f7a6 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -2362,7 +2362,7 @@ static int __rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer,
{
struct trace_buffer *buffer = cpu_buffer->buffer;
struct ring_buffer_cpu_meta *meta = NULL;
- struct buffer_page *bpage, *tmp;
+ struct buffer_page *bpage;
bool user_thread = current->mm != NULL;
struct ring_buffer_desc *desc = NULL;
long i;
@@ -2450,7 +2450,7 @@ static int __rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer,
return 0;
free_pages:
- list_for_each_entry_safe(bpage, tmp, pages, list) {
+ list_for_each_entry_mutable(bpage, pages, list) {
list_del_init(&bpage->list);
free_buffer_page(bpage);
}
@@ -2609,7 +2609,7 @@ rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu)
static void rb_free_cpu_buffer(struct ring_buffer_per_cpu *cpu_buffer)
{
struct list_head *head = cpu_buffer->pages;
- struct buffer_page *bpage, *tmp;
+ struct buffer_page *bpage;
irq_work_sync(&cpu_buffer->irq_work.work);
@@ -2621,7 +2621,7 @@ static void rb_free_cpu_buffer(struct ring_buffer_per_cpu *cpu_buffer)
if (head) {
rb_head_page_deactivate(cpu_buffer);
- list_for_each_entry_safe(bpage, tmp, head, list) {
+ list_for_each_entry_mutable(bpage, head, list) {
list_del_init(&bpage->list);
free_buffer_page(bpage);
}
@@ -3163,9 +3163,9 @@ rb_insert_pages(struct ring_buffer_per_cpu *cpu_buffer)
/* free pages if they weren't inserted */
if (!success) {
- struct buffer_page *bpage, *tmp;
- list_for_each_entry_safe(bpage, tmp, &cpu_buffer->new_pages,
- list) {
+ struct buffer_page *bpage;
+
+ list_for_each_entry_mutable(bpage, &cpu_buffer->new_pages, list) {
list_del_init(&bpage->list);
free_buffer_page(bpage);
}
@@ -3395,7 +3395,7 @@ int ring_buffer_resize(struct trace_buffer *buffer, unsigned long size,
out_err:
for_each_buffer_cpu(buffer, cpu) {
- struct buffer_page *bpage, *tmp;
+ struct buffer_page *bpage;
cpu_buffer = buffer->buffers[cpu];
cpu_buffer->nr_pages_to_update = 0;
@@ -3403,8 +3403,7 @@ int ring_buffer_resize(struct trace_buffer *buffer, unsigned long size,
if (list_empty(&cpu_buffer->new_pages))
continue;
- list_for_each_entry_safe(bpage, tmp, &cpu_buffer->new_pages,
- list) {
+ list_for_each_entry_mutable(bpage, &cpu_buffer->new_pages, list) {
list_del_init(&bpage->list);
free_buffer_page(bpage);
@@ -7316,7 +7315,7 @@ EXPORT_SYMBOL_GPL(ring_buffer_subbuf_order_get);
int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order)
{
struct ring_buffer_per_cpu *cpu_buffer;
- struct buffer_page *bpage, *tmp;
+ struct buffer_page *bpage;
int old_order, old_size;
int nr_pages;
int psize;
@@ -7436,7 +7435,7 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order)
raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags);
/* Free old sub buffers */
- list_for_each_entry_safe(bpage, tmp, &old_pages, list) {
+ list_for_each_entry_mutable(bpage, &old_pages, list) {
list_del_init(&bpage->list);
free_buffer_page(bpage);
}
@@ -7461,7 +7460,7 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order)
if (!cpu_buffer->nr_pages_to_update)
continue;
- list_for_each_entry_safe(bpage, tmp, &cpu_buffer->new_pages, list) {
+ list_for_each_entry_mutable(bpage, &cpu_buffer->new_pages, list) {
list_del_init(&bpage->list);
free_buffer_page(bpage);
}
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 1146b83b711a..f1049850e986 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -1380,7 +1380,7 @@ static int do_run_tracer_selftest(struct tracer *type)
static __init int init_trace_selftests(void)
{
- struct trace_selftests *p, *n;
+ struct trace_selftests *p;
struct tracer *t, **last;
int ret;
@@ -1394,7 +1394,7 @@ static __init int init_trace_selftests(void)
pr_info("Running postponed tracer tests:\n");
tracing_selftest_running = true;
- list_for_each_entry_safe(p, n, &postponed_selftests, list) {
+ list_for_each_entry_mutable(p, &postponed_selftests, list) {
/* This loop can take minutes when sanitizers are enabled, so
* lets make sure we allow RCU processing.
*/
@@ -1434,11 +1434,11 @@ static void __init apply_trace_boot_options(void);
static void free_tracers(struct trace_array *tr)
{
- struct tracers *t, *n;
+ struct tracers *t;
lockdep_assert_held(&trace_types_lock);
- list_for_each_entry_safe(t, n, &tr->tracers, list) {
+ list_for_each_entry_mutable(t, &tr->tracers, list) {
list_del(&t->list);
kfree(t->flags);
kfree(t);
@@ -6906,11 +6906,11 @@ void tracing_log_err(struct trace_array *tr,
static void clear_tracing_err_log(struct trace_array *tr)
{
- struct tracing_log_err *err, *next;
+ struct tracing_log_err *err;
guard(mutex)(&tracing_err_log_lock);
- list_for_each_entry_safe(err, next, &tr->err_log, list) {
+ list_for_each_entry_mutable(err, &tr->err_log, list) {
list_del(&err->list);
free_tracing_log_err(err);
}
diff --git a/kernel/trace/trace_dynevent.c b/kernel/trace/trace_dynevent.c
index c4dfbc293bae..9e076106bee7 100644
--- a/kernel/trace/trace_dynevent.c
+++ b/kernel/trace/trace_dynevent.c
@@ -100,7 +100,7 @@ int dyn_event_release(const char *raw_command, struct dyn_event_operations *type
return -EINVAL;
mutex_lock(&event_mutex);
- for_each_dyn_event_safe(pos, n) {
+ for_each_dyn_event_safe(pos) {
if (type && type != pos->ops)
continue;
if (!pos->ops->match(system, event,
@@ -207,7 +207,7 @@ static const struct seq_operations dyn_event_seq_op = {
*/
int dyn_events_release_all(struct dyn_event_operations *type)
{
- struct dyn_event *ev, *tmp;
+ struct dyn_event *ev;
int ret = 0;
mutex_lock(&event_mutex);
@@ -219,7 +219,7 @@ int dyn_events_release_all(struct dyn_event_operations *type)
goto out;
}
}
- for_each_dyn_event_safe(ev, tmp) {
+ for_each_dyn_event_safe(ev) {
if (type && ev->ops != type)
continue;
ret = ev->ops->free(ev);
diff --git a/kernel/trace/trace_dynevent.h b/kernel/trace/trace_dynevent.h
index beee3f8d7544..a4dc0812284f 100644
--- a/kernel/trace/trace_dynevent.h
+++ b/kernel/trace/trace_dynevent.h
@@ -115,10 +115,9 @@ int dyn_event_create(const char *raw_command, struct dyn_event_operations *type)
/*
* for_each_dyn_event - iterate over the dyn_event list safely
* @pos: the struct dyn_event * to use as a loop cursor
- * @n: the struct dyn_event * to use as temporary storage
*/
-#define for_each_dyn_event_safe(pos, n) \
- list_for_each_entry_safe(pos, n, &dyn_event_list, list)
+#define for_each_dyn_event_safe(pos) \
+ list_for_each_entry_mutable(pos, &dyn_event_list, list)
extern void dynevent_cmd_init(struct dynevent_cmd *cmd, char *buf, int maxlen,
enum dynevent_type type,
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index c46e623e7e0d..a34fb3e688d5 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -75,8 +75,7 @@ static int system_refcount_dec(struct event_subsystem *system)
#define do_for_each_event_file_safe(tr, file) \
list_for_each_entry(tr, &ftrace_trace_arrays, list) { \
- struct trace_event_file *___n; \
- list_for_each_entry_safe(file, ___n, &tr->events, list)
+ list_for_each_entry_mutable(file, &tr->events, list)
#define while_for_each_event_file() \
}
@@ -219,11 +218,11 @@ static int trace_define_common_fields(void)
static void trace_destroy_fields(struct trace_event_call *call)
{
- struct ftrace_event_field *field, *next;
+ struct ftrace_event_field *field;
struct list_head *head;
head = trace_get_fields(call);
- list_for_each_entry_safe(field, next, head, link) {
+ list_for_each_entry_mutable(field, head, link) {
list_del(&field->link);
kmem_cache_free(field_cachep, field);
}
@@ -928,9 +927,9 @@ static void free_event_mod(struct event_mod_load *event_mod)
static void clear_mod_events(struct trace_array *tr)
{
- struct event_mod_load *event_mod, *n;
+ struct event_mod_load *event_mod;
- list_for_each_entry_safe(event_mod, n, &tr->mod_events, list) {
+ list_for_each_entry_mutable(event_mod, &tr->mod_events, list) {
free_event_mod(event_mod);
}
}
@@ -938,10 +937,10 @@ static void clear_mod_events(struct trace_array *tr)
static int remove_cache_mod(struct trace_array *tr, const char *mod,
const char *match, const char *system, const char *event)
{
- struct event_mod_load *event_mod, *n;
+ struct event_mod_load *event_mod;
int ret = -EINVAL;
- list_for_each_entry_safe(event_mod, n, &tr->mod_events, list) {
+ list_for_each_entry_mutable(event_mod, &tr->mod_events, list) {
if (strcmp(event_mod->module, mod) != 0)
continue;
@@ -3557,7 +3556,7 @@ static void update_event_fields(struct trace_event_call *call,
/* Update all events for replacing eval and sanitizing */
void trace_event_update_all(struct trace_eval_map **map, int len)
{
- struct trace_event_call *call, *p;
+ struct trace_event_call *call;
const char *last_system = NULL;
bool first = false;
bool updated;
@@ -3565,7 +3564,7 @@ void trace_event_update_all(struct trace_eval_map **map, int len)
int i;
down_write(&trace_event_sem);
- list_for_each_entry_safe(call, p, &ftrace_events, list) {
+ list_for_each_entry_mutable(call, &ftrace_events, list) {
/* events are usually grouped together with systems */
if (!last_system || call->class->system != last_system) {
first = true;
@@ -3892,9 +3891,9 @@ EXPORT_SYMBOL_GPL(trace_remove_event_call);
#ifdef CONFIG_MODULES
static void update_mod_cache(struct trace_array *tr, struct module *mod)
{
- struct event_mod_load *event_mod, *n;
+ struct event_mod_load *event_mod;
- list_for_each_entry_safe(event_mod, n, &tr->mod_events, list) {
+ list_for_each_entry_mutable(event_mod, &tr->mod_events, list) {
if (strcmp(event_mod->module, mod->name) != 0)
continue;
@@ -3940,18 +3939,18 @@ static void trace_module_add_events(struct module *mod)
static void trace_module_remove_events(struct module *mod)
{
- struct trace_event_call *call, *p;
- struct module_string *modstr, *m;
+ struct trace_event_call *call;
+ struct module_string *modstr;
down_write(&trace_event_sem);
- list_for_each_entry_safe(call, p, &ftrace_events, list) {
+ list_for_each_entry_mutable(call, &ftrace_events, list) {
if ((call->flags & TRACE_EVENT_FL_DYNAMIC) || !call->module)
continue;
if (call->module == mod)
__trace_remove_event_call(call);
}
/* Check for any strings allocated for this module */
- list_for_each_entry_safe(modstr, m, &module_strings, next) {
+ list_for_each_entry_mutable(modstr, &module_strings, next) {
if (modstr->module != mod)
continue;
list_del(&modstr->next);
@@ -4483,9 +4482,9 @@ void __trace_early_add_events(struct trace_array *tr)
static void
__trace_remove_event_dirs(struct trace_array *tr)
{
- struct trace_event_file *file, *next;
+ struct trace_event_file *file;
- list_for_each_entry_safe(file, next, &tr->events, list)
+ list_for_each_entry_mutable(file, &tr->events, list)
remove_event_file_dir(file);
}
diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c
index 609325f57942..d82128084a87 100644
--- a/kernel/trace/trace_events_filter.c
+++ b/kernel/trace/trace_events_filter.c
@@ -1352,9 +1352,9 @@ struct filter_head {
static void free_filter_list(struct filter_head *filter_list)
{
- struct filter_list *filter_item, *tmp;
+ struct filter_list *filter_item;
- list_for_each_entry_safe(filter_item, tmp, &filter_list->list, list) {
+ list_for_each_entry_mutable(filter_item, &filter_list->list, list) {
__free_filter(filter_item->filter);
list_del(&filter_item->list);
kfree(filter_item);
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index 82ce492ab268..ff6016acab20 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -6765,7 +6765,7 @@ static bool hist_file_check_refs(struct trace_event_file *file)
static void hist_unreg_all(struct trace_event_file *file)
{
- struct event_trigger_data *test, *n;
+ struct event_trigger_data *test;
struct hist_trigger_data *hist_data;
struct synth_event *se;
const char *se_name;
@@ -6775,7 +6775,7 @@ static void hist_unreg_all(struct trace_event_file *file)
if (hist_file_check_refs(file))
return;
- list_for_each_entry_safe(test, n, &file->triggers, list) {
+ list_for_each_entry_mutable(test, &file->triggers, list) {
if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
hist_data = test->private_data;
list_del_rcu(&test->list);
@@ -7002,9 +7002,9 @@ hist_enable_trigger(struct event_trigger_data *data,
static void hist_enable_unreg_all(struct trace_event_file *file)
{
- struct event_trigger_data *test, *n;
+ struct event_trigger_data *test;
- list_for_each_entry_safe(test, n, &file->triggers, list) {
+ list_for_each_entry_mutable(test, &file->triggers, list) {
if (test->cmd_ops->trigger_type == ETT_HIST_ENABLE) {
list_del_rcu(&test->list);
update_cond_flag(file);
diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
index 655db2e82513..8654cd83f64d 100644
--- a/kernel/trace/trace_events_trigger.c
+++ b/kernel/trace/trace_events_trigger.c
@@ -40,7 +40,7 @@ static void trigger_create_kthread_locked(void)
static void trigger_data_free_queued_locked(void)
{
- struct event_trigger_data *data, *tmp;
+ struct event_trigger_data *data;
struct llist_node *llnodes;
lockdep_assert_held(&trigger_data_kthread_mutex);
@@ -51,14 +51,14 @@ static void trigger_data_free_queued_locked(void)
tracepoint_synchronize_unregister();
- llist_for_each_entry_safe(data, tmp, llnodes, llist)
+ llist_for_each_entry_mutable(data, llnodes, llist)
kfree(data);
}
/* Bulk garbage collection of event_trigger_data elements */
static int trigger_kthread_fn(void *ignore)
{
- struct event_trigger_data *data, *tmp;
+ struct event_trigger_data *data;
struct llist_node *llnodes;
/* Once this task starts, it lives forever */
@@ -74,7 +74,7 @@ static int trigger_kthread_fn(void *ignore)
/* make sure current triggers exit before free */
tracepoint_synchronize_unregister();
- llist_for_each_entry_safe(data, tmp, llnodes, llist)
+ llist_for_each_entry_mutable(data, llnodes, llist)
kfree(data);
}
@@ -477,11 +477,11 @@ __init int register_event_command(struct event_command *cmd)
*/
__init int unregister_event_command(struct event_command *cmd)
{
- struct event_command *p, *n;
+ struct event_command *p;
guard(mutex)(&trigger_cmd_mutex);
- list_for_each_entry_safe(p, n, &trigger_commands, list) {
+ list_for_each_entry_mutable(p, &trigger_commands, list) {
if (strcmp(cmd->name, p->name) == 0) {
list_del_init(&p->list);
return 0;
@@ -632,8 +632,9 @@ clear_event_triggers(struct trace_array *tr)
struct trace_event_file *file;
list_for_each_entry(file, &tr->events, list) {
- struct event_trigger_data *data, *n;
- list_for_each_entry_safe(data, n, &file->triggers, list) {
+ struct event_trigger_data *data;
+
+ list_for_each_entry_mutable(data, &file->triggers, list) {
trace_event_trigger_enable_disable(file, 0);
list_del_rcu(&data->list);
if (data->cmd_ops->free)
diff --git a/kernel/trace/trace_events_user.c b/kernel/trace/trace_events_user.c
index c4ba484f7b38..090d645eebf0 100644
--- a/kernel/trace/trace_events_user.c
+++ b/kernel/trace/trace_events_user.c
@@ -761,9 +761,9 @@ static struct user_event_mm *current_user_event_mm(void)
static void user_event_mm_destroy(struct user_event_mm *mm)
{
- struct user_event_enabler *enabler, *next;
+ struct user_event_enabler *enabler;
- list_for_each_entry_safe(enabler, next, &mm->enablers, mm_enablers_link)
+ list_for_each_entry_mutable(enabler, &mm->enablers, mm_enablers_link)
user_event_enabler_destroy(enabler, false);
mmdrop(mm->mm);
@@ -1085,10 +1085,10 @@ static int user_field_size(const char *type)
static void user_event_destroy_validators(struct user_event *user)
{
- struct user_event_validator *validator, *next;
+ struct user_event_validator *validator;
struct list_head *head = &user->validators;
- list_for_each_entry_safe(validator, next, head, user_event_link) {
+ list_for_each_entry_mutable(validator, head, user_event_link) {
list_del(&validator->user_event_link);
kfree(validator);
}
@@ -1096,10 +1096,10 @@ static void user_event_destroy_validators(struct user_event *user)
static void user_event_destroy_fields(struct user_event *user)
{
- struct ftrace_event_field *field, *next;
+ struct ftrace_event_field *field;
struct list_head *head = &user->fields;
- list_for_each_entry_safe(field, next, head, link) {
+ list_for_each_entry_mutable(field, head, link) {
list_del(&field->link);
kfree(field);
}
@@ -2611,7 +2611,7 @@ static long user_events_ioctl_unreg(unsigned long uarg)
{
struct user_unreg __user *ureg = (struct user_unreg __user *)uarg;
struct user_event_mm *mm = current->user_event_mm;
- struct user_event_enabler *enabler, *next;
+ struct user_event_enabler *enabler;
struct user_unreg reg;
unsigned long flags;
long ret;
@@ -2636,7 +2636,7 @@ static long user_events_ioctl_unreg(unsigned long uarg)
*/
mutex_lock(&event_mutex);
- list_for_each_entry_safe(enabler, next, &mm->enablers, mm_enablers_link) {
+ list_for_each_entry_mutable(enabler, &mm->enablers, mm_enablers_link) {
if (enabler->addr == reg.disable_addr &&
ENABLE_BIT(enabler) == reg.disable_bit) {
set_bit(ENABLE_VAL_FREEING_BIT, ENABLE_BITOPS(enabler));
diff --git a/kernel/trace/trace_stat.c b/kernel/trace/trace_stat.c
index 856ece13b7dc..3240b1ff7418 100644
--- a/kernel/trace/trace_stat.c
+++ b/kernel/trace/trace_stat.c
@@ -344,10 +344,10 @@ int register_stat_tracer(struct tracer_stat *trace)
void unregister_stat_tracer(struct tracer_stat *trace)
{
- struct stat_session *node, *tmp;
+ struct stat_session *node;
mutex_lock(&all_stat_sessions_mutex);
- list_for_each_entry_safe(node, tmp, &all_stat_sessions, session_list) {
+ list_for_each_entry_mutable(node, &all_stat_sessions, session_list) {
if (node->ts == trace) {
list_del(&node->session_list);
destroy_session(node);
diff --git a/kernel/user-return-notifier.c b/kernel/user-return-notifier.c
index 870ecd7c63ed..9f1dcd72b756 100644
--- a/kernel/user-return-notifier.c
+++ b/kernel/user-return-notifier.c
@@ -35,11 +35,10 @@ EXPORT_SYMBOL_GPL(user_return_notifier_unregister);
void fire_user_return_notifiers(void)
{
struct user_return_notifier *urn;
- struct hlist_node *tmp2;
struct hlist_head *head;
head = &get_cpu_var(return_notifier_list);
- hlist_for_each_entry_safe(urn, tmp2, head, link)
+ hlist_for_each_entry_mutable(urn, head, link)
urn->on_user_return(urn);
put_cpu_var(return_notifier_list);
}
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 78f25afb4a9d..8fbdf1664c38 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -1167,7 +1167,7 @@ static void move_linked_works(struct work_struct *work, struct list_head *head,
* Linked worklist will always end before the end of the list,
* use NULL for list head.
*/
- list_for_each_entry_safe_from(work, n, NULL, entry) {
+ list_for_each_entry_mutable_from(work, n, NULL, entry) {
list_move_tail(&work->entry, head);
if (!(*work_data_bits(work) & WORK_STRUCT_LINKED))
break;
@@ -1193,7 +1193,7 @@ static void move_linked_works(struct work_struct *work, struct list_head *head,
*
* If @nextp is not NULL, it's updated to point to the next work of the last
* scheduled work. This allows assign_work() to be nested inside
- * list_for_each_entry_safe().
+ * list_for_each_entry_mutable().
*
* Returns %true if @work was successfully assigned to @worker. %false if @work
* was punted to another worker already executing it.
@@ -2912,9 +2912,9 @@ static void detach_dying_workers(struct list_head *cull_list)
static void reap_dying_workers(struct list_head *cull_list)
{
- struct worker *worker, *tmp;
+ struct worker *worker;
- list_for_each_entry_safe(worker, tmp, cull_list, entry) {
+ list_for_each_entry_mutable(worker, cull_list, entry) {
list_del_init(&worker->entry);
kthread_stop_put(worker->task);
kfree(worker);
@@ -3550,7 +3550,7 @@ static bool assign_rescuer_work(struct pool_workqueue *pwq, struct worker *rescu
work = list_next_entry(cursor, entry);
/* find the next work item to rescue */
- list_for_each_entry_safe_from(work, n, &pool->worklist, entry) {
+ list_for_each_entry_mutable_from(work, n, &pool->worklist, entry) {
if (get_work_pwq(work) == pwq && assign_work(work, rescuer, &n)) {
pwq->stats[PWQ_STAT_RESCUED]++;
/* put the cursor for next search */
@@ -4157,7 +4157,7 @@ void __flush_workqueue(struct workqueue_struct *wq)
struct wq_flusher *next, *tmp;
/* complete all the flushers sharing the current flush color */
- list_for_each_entry_safe(next, tmp, &wq->flusher_queue, list) {
+ list_for_each_entry_mutable(next, &wq->flusher_queue, list) {
if (next->flush_color != wq->flush_color)
break;
list_del_init(&next->list);
@@ -7080,7 +7080,7 @@ static int workqueue_apply_unbound_cpumask(const cpumask_var_t unbound_cpumask)
LIST_HEAD(ctxs);
int ret = 0;
struct workqueue_struct *wq;
- struct apply_wqattrs_ctx *ctx, *n;
+ struct apply_wqattrs_ctx *ctx;
lockdep_assert_held(&wq_pool_mutex);
@@ -7097,7 +7097,7 @@ static int workqueue_apply_unbound_cpumask(const cpumask_var_t unbound_cpumask)
list_add_tail(&ctx->list, &ctxs);
}
- list_for_each_entry_safe(ctx, n, &ctxs, list) {
+ list_for_each_entry_mutable(ctx, &ctxs, list) {
if (!ret)
apply_wqattrs_commit(ctx);
apply_wqattrs_cleanup(ctx);
--
2.43.0
^ permalink raw reply related
* [PATCH v3 4/7] block: Use mutable list iterators
From: Kaitao Cheng @ 2026-06-22 4:42 UTC (permalink / raw)
To: Yu Kuai, Jens Axboe, Tejun Heo, Josef Bacik,
Richard Russon (FlatCap), Jonathan Derrick
Cc: linux-block, linux-kernel, cgroups, linux-ntfs-dev, Kaitao Cheng
In-Reply-To: <20260622040533.29824-1-kaitao.cheng@linux.dev>
From: Kaitao Cheng <chengkaitao@kylinos.cn>
The safe list iterators require callers to provide a temporary cursor
even when the cursor is only used by the iterator itself. The mutable
iterator variants keep the same removal-safe traversal semantics while
allowing those internal cursors to be hidden from the call sites.
Convert block users of list, hlist and llist safe iterators to the new
mutable helpers. Drop the now-unused temporary cursor variables where
the loop body does not inspect or reset them.
This is a mechanical cleanup with no intended change in traversal order
or list mutation behavior.
Signed-off-by: Kaitao Cheng <chengkaitao@kylinos.cn>
---
block/bfq-iosched.c | 17 +++++++----------
block/blk-cgroup.c | 12 ++++++------
block/blk-flush.c | 4 ++--
block/blk-iocost.c | 18 +++++++++---------
block/blk-mq.c | 8 ++++----
block/blk-throttle.c | 4 ++--
block/kyber-iosched.c | 4 ++--
block/partitions/ldm.c | 8 ++++----
block/sed-opal.c | 4 ++--
9 files changed, 38 insertions(+), 41 deletions(-)
diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c
index 141c602d5e85..78e32ba0553d 100644
--- a/block/bfq-iosched.c
+++ b/block/bfq-iosched.c
@@ -1209,9 +1209,8 @@ static int bfqq_process_refs(struct bfq_queue *bfqq)
static void bfq_reset_burst_list(struct bfq_data *bfqd, struct bfq_queue *bfqq)
{
struct bfq_queue *item;
- struct hlist_node *n;
- hlist_for_each_entry_safe(item, n, &bfqd->burst_list, burst_list_node)
+ hlist_for_each_entry_mutable(item, &bfqd->burst_list, burst_list_node)
hlist_del_init(&item->burst_list_node);
/*
@@ -1236,7 +1235,6 @@ static void bfq_add_to_burst(struct bfq_data *bfqd, struct bfq_queue *bfqq)
if (bfqd->burst_size == bfqd->bfq_large_burst_thresh) {
struct bfq_queue *pos, *bfqq_item;
- struct hlist_node *n;
/*
* Enough queues have been activated shortly after each
@@ -1260,8 +1258,8 @@ static void bfq_add_to_burst(struct bfq_data *bfqd, struct bfq_queue *bfqq)
* belonging to a large burst. So the burst list is not
* needed any more. Remove it.
*/
- hlist_for_each_entry_safe(pos, n, &bfqd->burst_list,
- burst_list_node)
+ hlist_for_each_entry_mutable(pos, &bfqd->burst_list,
+ burst_list_node)
hlist_del_init(&pos->burst_list_node);
} else /*
* Burst not yet large: add bfqq to the burst list. Do
@@ -5330,7 +5328,6 @@ static struct request *bfq_dispatch_request(struct blk_mq_hw_ctx *hctx)
void bfq_put_queue(struct bfq_queue *bfqq)
{
struct bfq_queue *item;
- struct hlist_node *n;
struct bfq_group *bfqg = bfqq_group(bfqq);
bfq_log_bfqq(bfqq->bfqd, bfqq, "put_queue: %p %d", bfqq, bfqq->ref);
@@ -5391,8 +5388,8 @@ void bfq_put_queue(struct bfq_queue *bfqq)
hlist_del_init(&bfqq->woken_list_node);
/* reset waker for all queues in woken list */
- hlist_for_each_entry_safe(item, n, &bfqq->woken_list,
- woken_list_node) {
+ hlist_for_each_entry_mutable(item, &bfqq->woken_list,
+ woken_list_node) {
item->waker_bfqq = NULL;
hlist_del_init(&item->woken_list_node);
}
@@ -7141,13 +7138,13 @@ static void bfq_depth_updated(struct request_queue *q)
static void bfq_exit_queue(struct elevator_queue *e)
{
struct bfq_data *bfqd = e->elevator_data;
- struct bfq_queue *bfqq, *n;
+ struct bfq_queue *bfqq;
unsigned int actuator;
hrtimer_cancel(&bfqd->idle_slice_timer);
spin_lock_irq(&bfqd->lock);
- list_for_each_entry_safe(bfqq, n, &bfqd->idle_list, bfqq_list)
+ list_for_each_entry_mutable(bfqq, &bfqd->idle_list, bfqq_list)
bfq_deactivate_bfqq(bfqd, bfqq, false, false);
spin_unlock_irq(&bfqd->lock);
diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c
index 3093c1c03902..ced900019f2e 100644
--- a/block/blk-cgroup.c
+++ b/block/blk-cgroup.c
@@ -997,7 +997,7 @@ static void __blkcg_rstat_flush(struct blkcg *blkcg, int cpu)
{
struct llist_head *lhead = per_cpu_ptr(blkcg->lhead, cpu);
struct llist_node *lnode;
- struct blkg_iostat_set *bisc, *next_bisc;
+ struct blkg_iostat_set *bisc;
unsigned long flags;
rcu_read_lock();
@@ -1017,17 +1017,17 @@ static void __blkcg_rstat_flush(struct blkcg *blkcg, int cpu)
/*
* Iterate only the iostat_cpu's queued in the lockless list.
*/
- llist_for_each_entry_safe(bisc, next_bisc, lnode, lnode) {
+ llist_for_each_entry_mutable(bisc, lnode, lnode) {
struct blkcg_gq *blkg = bisc->blkg;
struct blkcg_gq *parent = blkg->parent;
struct blkg_iostat cur;
unsigned int seq;
/*
- * Order assignment of `next_bisc` from `bisc->lnode.next` in
- * llist_for_each_entry_safe and clearing `bisc->lqueued` for
- * avoiding to assign `next_bisc` with new next pointer added
- * in blk_cgroup_bio_start() in case of re-ordering.
+ * Order the iterator's internal `bisc->lnode.next` load before
+ * clearing `bisc->lqueued`, so the iterator can't pick up a new
+ * next pointer added in blk_cgroup_bio_start() in case of
+ * re-ordering.
*
* The pair barrier is implied in llist_add() in blk_cgroup_bio_start().
*/
diff --git a/block/blk-flush.c b/block/blk-flush.c
index 403a46c86411..20654c2103f2 100644
--- a/block/blk-flush.c
+++ b/block/blk-flush.c
@@ -204,7 +204,7 @@ static enum rq_end_io_ret flush_end_io(struct request *flush_rq,
{
struct request_queue *q = flush_rq->q;
struct list_head *running;
- struct request *rq, *n;
+ struct request *rq;
unsigned long flags = 0;
struct blk_flush_queue *fq = blk_get_flush_queue(flush_rq->mq_ctx);
@@ -243,7 +243,7 @@ static enum rq_end_io_ret flush_end_io(struct request *flush_rq,
fq->flush_running_idx ^= 1;
/* and push the waiting requests to the next stage */
- list_for_each_entry_safe(rq, n, running, queuelist) {
+ list_for_each_entry_mutable(rq, running, queuelist) {
unsigned int seq = blk_flush_cur_seq(rq);
BUG_ON(seq != REQ_FSEQ_PREFLUSH && seq != REQ_FSEQ_POSTFLUSH);
diff --git a/block/blk-iocost.c b/block/blk-iocost.c
index 563cc7dcf348..2ca18e52bc13 100644
--- a/block/blk-iocost.c
+++ b/block/blk-iocost.c
@@ -1718,7 +1718,7 @@ static void iocg_flush_stat_leaf(struct ioc_gq *iocg, struct ioc_now *now)
static void iocg_flush_stat(struct list_head *target_iocgs, struct ioc_now *now)
{
LIST_HEAD(inner_walk);
- struct ioc_gq *iocg, *tiocg;
+ struct ioc_gq *iocg;
/* flush leaves and build inner node walk list */
list_for_each_entry(iocg, target_iocgs, active_list) {
@@ -1727,7 +1727,7 @@ static void iocg_flush_stat(struct list_head *target_iocgs, struct ioc_now *now)
}
/* keep flushing upwards by walking the inner list backwards */
- list_for_each_entry_safe_reverse(iocg, tiocg, &inner_walk, walk_list) {
+ list_for_each_entry_mutable_reverse(iocg, &inner_walk, walk_list) {
iocg_flush_stat_upward(iocg);
list_del_init(&iocg->walk_list);
}
@@ -1848,7 +1848,7 @@ static void transfer_surpluses(struct list_head *surpluses, struct ioc_now *now)
{
LIST_HEAD(over_hwa);
LIST_HEAD(inner_walk);
- struct ioc_gq *iocg, *tiocg, *root_iocg;
+ struct ioc_gq *iocg, *root_iocg;
u32 after_sum, over_sum, over_target, gamma;
/*
@@ -1884,7 +1884,7 @@ static void transfer_surpluses(struct list_head *surpluses, struct ioc_now *now)
over_target = 0;
}
- list_for_each_entry_safe(iocg, tiocg, &over_hwa, walk_list) {
+ list_for_each_entry_mutable(iocg, &over_hwa, walk_list) {
if (over_target)
iocg->hweight_after_donation =
div_u64((u64)iocg->hweight_after_donation *
@@ -2055,7 +2055,7 @@ static void transfer_surpluses(struct list_head *surpluses, struct ioc_now *now)
}
/* walk list should be dissolved after use */
- list_for_each_entry_safe(iocg, tiocg, &inner_walk, walk_list)
+ list_for_each_entry_mutable(iocg, &inner_walk, walk_list)
list_del_init(&iocg->walk_list);
}
@@ -2166,9 +2166,9 @@ static void ioc_forgive_debts(struct ioc *ioc, u64 usage_us_sum, int nr_debtors,
static int ioc_check_iocgs(struct ioc *ioc, struct ioc_now *now)
{
int nr_debtors = 0;
- struct ioc_gq *iocg, *tiocg;
+ struct ioc_gq *iocg;
- list_for_each_entry_safe(iocg, tiocg, &ioc->active_iocgs, active_list) {
+ list_for_each_entry_mutable(iocg, &ioc->active_iocgs, active_list) {
if (!waitqueue_active(&iocg->waitq) && !iocg->abs_vdebt &&
!iocg->delay && !iocg_is_idle(iocg))
continue;
@@ -2234,7 +2234,7 @@ static int ioc_check_iocgs(struct ioc *ioc, struct ioc_now *now)
static void ioc_timer_fn(struct timer_list *timer)
{
struct ioc *ioc = container_of(timer, struct ioc, timer);
- struct ioc_gq *iocg, *tiocg;
+ struct ioc_gq *iocg;
struct ioc_now now;
LIST_HEAD(surpluses);
int nr_debtors, nr_shortages = 0, nr_lagging = 0;
@@ -2378,7 +2378,7 @@ static void ioc_timer_fn(struct timer_list *timer)
commit_weights(ioc);
/* surplus list should be dissolved after use */
- list_for_each_entry_safe(iocg, tiocg, &surpluses, surplus_list)
+ list_for_each_entry_mutable(iocg, &surpluses, surplus_list)
list_del_init(&iocg->surplus_list);
/*
diff --git a/block/blk-mq.c b/block/blk-mq.c
index 88cb5acc4f39..2daed45ad4e7 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -1216,9 +1216,9 @@ EXPORT_SYMBOL_GPL(blk_mq_end_request_batch);
static void blk_complete_reqs(struct llist_head *list)
{
struct llist_node *entry = llist_reverse_order(llist_del_all(list));
- struct request *rq, *next;
+ struct request *rq;
- llist_for_each_entry_safe(rq, next, entry, ipi_list)
+ llist_for_each_entry_mutable(rq, entry, ipi_list)
rq->q->mq_ops->complete(rq);
}
@@ -4383,14 +4383,14 @@ static int blk_mq_alloc_ctxs(struct request_queue *q)
*/
void blk_mq_release(struct request_queue *q)
{
- struct blk_mq_hw_ctx *hctx, *next;
+ struct blk_mq_hw_ctx *hctx;
unsigned long i;
queue_for_each_hw_ctx(q, hctx, i)
WARN_ON_ONCE(hctx && list_empty(&hctx->hctx_list));
/* all hctx are in .unused_hctx_list now */
- list_for_each_entry_safe(hctx, next, &q->unused_hctx_list, hctx_list) {
+ list_for_each_entry_mutable(hctx, &q->unused_hctx_list, hctx_list) {
list_del_init(&hctx->hctx_list);
kobject_put(&hctx->kobj);
}
diff --git a/block/blk-throttle.c b/block/blk-throttle.c
index 47052ba21d1b..1dd4901f00f3 100644
--- a/block/blk-throttle.c
+++ b/block/blk-throttle.c
@@ -1686,10 +1686,10 @@ static void tg_cancel_writeback_bios(struct throtl_grp *tg,
tg->flags |= THROTL_TG_CANCELING;
for (rw = READ; rw <= WRITE; rw++) {
- struct throtl_qnode *qn, *tmp;
+ struct throtl_qnode *qn;
unsigned int nr_bios = 0;
- list_for_each_entry_safe(qn, tmp, &sq->queued[rw], node) {
+ list_for_each_entry_mutable(qn, &sq->queued[rw], node) {
struct bio *bio;
while ((bio = bio_list_pop(&qn->bios_iops))) {
diff --git a/block/kyber-iosched.c b/block/kyber-iosched.c
index 971818bcdc9d..1a509666b861 100644
--- a/block/kyber-iosched.c
+++ b/block/kyber-iosched.c
@@ -578,9 +578,9 @@ static void kyber_insert_requests(struct blk_mq_hw_ctx *hctx,
blk_insert_t flags)
{
struct kyber_hctx_data *khd = hctx->sched_data;
- struct request *rq, *next;
+ struct request *rq;
- list_for_each_entry_safe(rq, next, rq_list, queuelist) {
+ list_for_each_entry_mutable(rq, rq_list, queuelist) {
unsigned int sched_domain = kyber_sched_domain(rq->cmd_flags);
struct kyber_ctx_queue *kcq = &khd->kcqs[rq->mq_ctx->index_hw[hctx->type]];
struct list_head *head = &kcq->rq_list[sched_domain];
diff --git a/block/partitions/ldm.c b/block/partitions/ldm.c
index c0bdcae58a3e..459f72f2148a 100644
--- a/block/partitions/ldm.c
+++ b/block/partitions/ldm.c
@@ -1285,11 +1285,11 @@ static bool ldm_frag_add (const u8 *data, int size, struct list_head *frags)
*/
static void ldm_frag_free (struct list_head *list)
{
- struct list_head *item, *tmp;
+ struct list_head *item;
BUG_ON (!list);
- list_for_each_safe (item, tmp, list)
+ list_for_each_mutable(item, list)
kfree (list_entry (item, struct frag, list));
}
@@ -1400,11 +1400,11 @@ static bool ldm_get_vblks(struct parsed_partitions *state, unsigned long base,
*/
static void ldm_free_vblks (struct list_head *lh)
{
- struct list_head *item, *tmp;
+ struct list_head *item;
BUG_ON (!lh);
- list_for_each_safe (item, tmp, lh)
+ list_for_each_mutable(item, lh)
kfree (list_entry (item, struct vblk, list));
}
diff --git a/block/sed-opal.c b/block/sed-opal.c
index 79b290d9458a..5bf9ebce8452 100644
--- a/block/sed-opal.c
+++ b/block/sed-opal.c
@@ -2573,10 +2573,10 @@ static int check_opal_support(struct opal_dev *dev)
static void clean_opal_dev(struct opal_dev *dev)
{
- struct opal_suspend_data *suspend, *next;
+ struct opal_suspend_data *suspend;
mutex_lock(&dev->dev_lock);
- list_for_each_entry_safe(suspend, next, &dev->unlk_lst, node) {
+ list_for_each_entry_mutable(suspend, &dev->unlk_lst, node) {
list_del(&suspend->node);
kfree(suspend);
}
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v3 5/7] kernel: Use mutable list iterators
From: bot+bpf-ci @ 2026-06-22 5:22 UTC (permalink / raw)
To: kaitao.cheng, paul, eparis, ast, daniel, andrii, eddyz87, memxor,
davem, kuba, hawk, john.fastabend, tj, hannes, mkoutny, dev,
mripard, natalie.vock, peterz, mingo, acme, namhyung, mhiramat,
oleg, oberpar, akpm, baoquan.he, rppt, pasha.tatashin, pratyush,
naveen, jpoimboe, jikos, mbenes, pmladek, will, boqun, mcgrof,
petr.pavlu, da.gomez, samitolvanen, steffen.klassert,
daniel.m.jordan, rafael, dave, paulmck, josh, frederic,
neeraj.upadhyay, joelagnelf, urezki, juri.lelli, vincent.guittot,
kees, bsingharora, anna-maria, tglx, jstultz, kpsingh,
mattbobrowski, nathan, martin.lau, song, mark.rutland,
mathieu.desnoyers, dietmar.eggemann, void, rostedt
Cc: audit, linux-kernel, bpf, netdev, cgroups, dri-devel,
linux-perf-users, linux-trace-kernel, kexec, live-patching,
linux-modules, linux-crypto, linux-pm, rcu, sched-ext, llvm,
chengkaitao, ast, andrii, daniel, martin.lau, eddyz87,
yonghong.song, clm, ihor.solodrai
In-Reply-To: <20260622042811.31684-1-kaitao.cheng@linux.dev>
[-- Attachment #1: Type: text/plain, Size: 1983 bytes --]
> diff --git a/kernel/trace/trace_dynevent.c b/kernel/trace/trace_dynevent.c
> index c4dfbc293bae..9e076106bee7 100644
> --- a/kernel/trace/trace_dynevent.c
> +++ b/kernel/trace/trace_dynevent.c
[ ... ]
> @@ -100,7 +100,7 @@ int dyn_event_release(const char *raw_command, struct dyn_event_operations *type
> return -EINVAL;
>
> mutex_lock(&event_mutex);
> - for_each_dyn_event_safe(pos, n) {
> + for_each_dyn_event_safe(pos) {
> if (type && type != pos->ops)
> continue;
> if (!pos->ops->match(system, event,
Does this leave the variable n unused? Looking at the function
declaration around line 74, it still has:
struct dyn_event *pos, *n;
but after this change n is no longer passed to for_each_dyn_event_safe().
The macro now hides the temporary cursor internally, so n appears to be
dead code. This would trigger -Wunused-variable warnings and break builds
with CONFIG_WERROR=y.
For comparison, the sibling change at line 219 (dyn_events_release_all)
correctly removed the *tmp variable from its declaration.
> diff --git a/kernel/trace/trace_dynevent.h b/kernel/trace/trace_dynevent.h
> index beee3f8d7544..a4dc0812284f 100644
> --- a/kernel/trace/trace_dynevent.h
> +++ b/kernel/trace/trace_dynevent.h
> @@ -115,10 +115,9 @@ int dyn_event_create(const char *raw_command, struct dyn_event_operations *type)
> /*
> * for_each_dyn_event - iterate over the dyn_event list safely
> * @pos: the struct dyn_event * to use as a loop cursor
> - * @n: the struct dyn_event * to use as temporary storage
> */
> -#define for_each_dyn_event_safe(pos, n) \
> - list_for_each_entry_safe(pos, n, &dyn_event_list, list)
> +#define for_each_dyn_event_safe(pos) \
> + list_for_each_entry_mutable(pos, &dyn_event_list, list)
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/27930295027
^ permalink raw reply
* Re: [PATCH v3 0/7] Prepare mutable list iterators to cache cursor state
From: Alexei Starovoitov @ 2026-06-22 5:28 UTC (permalink / raw)
To: Kaitao Cheng
Cc: Andrew Morton, David Hildenbrand, Jens Axboe, Tejun Heo,
Alexander Viro, Christian Brauner, Alexei Starovoitov,
Daniel Borkmann, Andrii Nakryiko, Johannes Weiner, Peter Zijlstra,
Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim,
Thomas Gleixner, Juri Lelli, Vincent Guittot, Paul Moore,
Andy Shevchenko, Paul E. McKenney, Shakeel Butt,
Christian König, David Howells, Simona Vetter, Randy Dunlap,
Luca Ceresoli, Philipp Stanner, linux-block, LKML,
open list:CONTROL GROUP (CGROUP), linux-ntfs-dev, Linux-Fsdevel,
io-uring, audit, bpf, Network Development, dri-devel,
linux-perf-use., linux-trace-kernel, kexec, live-patching,
linux-modules, Linux Crypto Mailing List, Linux Power Management,
rcu, sched-ext, linux-mm, virtualization, damon,
clang-built-linux, chengkaitao
In-Reply-To: <20260622040533.29824-1-kaitao.cheng@linux.dev>
On Sun, Jun 21, 2026 at 9:06 PM Kaitao Cheng <kaitao.cheng@linux.dev> wrote:
>
> From: chengkaitao <chengkaitao@kylinos.cn>
>
> The list_for_each*_safe() helpers are used when the loop body may remove
> the current entry. Their current interface, however, forces every caller
> to define a temporary cursor outside the macro and pass it in, even when
> the caller never uses that cursor directly. For most call sites this
> extra cursor is just boilerplate required by the macro implementation.
>
> This is awkward because the saved next pointer is an internal detail of
> the iteration. Callers that only remove or move the current entry do not
> need to spell it out.
>
> The _safe() suffix has also caused confusion. Christian Koenig pointed
> out that the name is easy to read as a thread-safe variant, especially
> for beginners, even though it only means that the iterator keeps enough
> state to tolerate removal of the current entry. He suggested _mutable()
> as a clearer description of what the loop permits.
>
> Add *_mutable() iterator variants for list, hlist and llist. The new
> helpers are variadic and support both forms. In the common case, the
> caller omits the temporary cursor and the macro creates a unique internal
> cursor with typeof(pos) and __UNIQUE_ID(). If a loop really needs an
> explicit temporary cursor, the caller can still pass it and the helper
> keeps the existing *_safe() behaviour.
>
> For example, a call site may use the shorter form:
>
> list_for_each_entry_mutable(pos, head, member)
>
> or keep the explicit temporary cursor form:
>
> list_for_each_entry_mutable(pos, tmp, head, member)
>
> The existing *_safe() helpers remain available for compatibility. This
> series only converts users in mm, block, kernel, init and io_uring. If
> this approach looks acceptable, the remaining users can be converted in
> follow-up series.
>
> Changes in v3 (Christian König, Andy Shevchenko):
> - Convert safe list walks to mutable iterators
>
> Changes in v2 (Muchun Song, Andy Shevchenko):
> - Drop the list_for_each_entry_mutable*() helpers from v1 and make the
> cursor change directly in the existing list_for_each_entry*() helpers.
> - Open-code special list walks that rely on updating the loop cursor in
> the body, preserving their existing traversal semantics.
>
> Link to v2:
> https://lore.kernel.org/all/20260609061347.93688-1-kaitao.cheng@linux.dev/
>
> Link to v1:
> https://lore.kernel.org/all/20260529082149.76764-1-kaitao.cheng@linux.dev/
>
> Kaitao Cheng (7):
> list: Add mutable iterator variants
> llist: Add mutable iterator variants
> mm: Use mutable list iterators
> block: Use mutable list iterators
> kernel: Use mutable list iterators
> initramfs: Use mutable list iterator
> io_uring: Use mutable list iterators
>
> block/bfq-iosched.c | 17 +-
> block/blk-cgroup.c | 12 +-
> block/blk-flush.c | 4 +-
> block/blk-iocost.c | 18 +-
> block/blk-mq.c | 8 +-
> block/blk-throttle.c | 4 +-
> block/kyber-iosched.c | 4 +-
> block/partitions/ldm.c | 8 +-
> block/sed-opal.c | 4 +-
> include/linux/list.h | 269 ++++++++++++++++++++++++----
> include/linux/llist.h | 81 +++++++--
> init/initramfs.c | 5 +-
> io_uring/cancel.c | 6 +-
> io_uring/poll.c | 3 +-
> io_uring/rw.c | 4 +-
> io_uring/timeout.c | 8 +-
> io_uring/uring_cmd.c | 3 +-
> kernel/audit_tree.c | 4 +-
> kernel/audit_watch.c | 16 +-
> kernel/auditfilter.c | 4 +-
> kernel/auditsc.c | 4 +-
> kernel/bpf/arena.c | 10 +-
> kernel/bpf/arraymap.c | 8 +-
> kernel/bpf/bpf_local_storage.c | 3 +-
> kernel/bpf/bpf_lru_list.c | 25 ++-
> kernel/bpf/btf.c | 18 +-
> kernel/bpf/cgroup.c | 7 +-
> kernel/bpf/cpumap.c | 4 +-
> kernel/bpf/devmap.c | 10 +-
> kernel/bpf/helpers.c | 8 +-
> kernel/bpf/local_storage.c | 4 +-
> kernel/bpf/memalloc.c | 16 +-
> kernel/bpf/offload.c | 8 +-
> kernel/bpf/states.c | 4 +-
> kernel/bpf/stream.c | 4 +-
> kernel/bpf/verifier.c | 6 +-
> kernel/cgroup/cgroup-v1.c | 4 +-
> kernel/cgroup/cgroup.c | 54 +++---
> kernel/cgroup/dmem.c | 12 +-
> kernel/cgroup/rdma.c | 8 +-
> kernel/events/core.c | 44 +++--
> kernel/events/uprobes.c | 12 +-
> kernel/exit.c | 8 +-
> kernel/fail_function.c | 4 +-
> kernel/gcov/clang.c | 4 +-
> kernel/irq_work.c | 4 +-
> kernel/kexec_core.c | 4 +-
> kernel/kprobes.c | 16 +-
> kernel/livepatch/core.c | 4 +-
> kernel/livepatch/core.h | 4 +-
> kernel/liveupdate/kho_block.c | 4 +-
> kernel/liveupdate/luo_flb.c | 4 +-
> kernel/locking/rwsem.c | 2 +-
> kernel/locking/test-ww_mutex.c | 2 +-
> kernel/module/main.c | 11 +-
> kernel/padata.c | 4 +-
> kernel/power/snapshot.c | 8 +-
> kernel/power/wakelock.c | 4 +-
> kernel/printk/printk.c | 11 +-
> kernel/ptrace.c | 4 +-
> kernel/rcu/rcutorture.c | 3 +-
> kernel/rcu/tasks.h | 9 +-
> kernel/rcu/tree.c | 6 +-
> kernel/resource.c | 4 +-
> kernel/sched/core.c | 4 +-
> kernel/sched/ext.c | 22 +--
> kernel/sched/fair.c | 28 +--
> kernel/sched/topology.c | 4 +-
> kernel/sched/wait.c | 4 +-
> kernel/seccomp.c | 4 +-
> kernel/signal.c | 11 +-
> kernel/smp.c | 4 +-
> kernel/taskstats.c | 8 +-
> kernel/time/clockevents.c | 6 +-
> kernel/time/clocksource.c | 4 +-
> kernel/time/posix-cpu-timers.c | 4 +-
> kernel/time/posix-timers.c | 3 +-
> kernel/torture.c | 3 +-
> kernel/trace/bpf_trace.c | 4 +-
> kernel/trace/ftrace.c | 49 +++--
> kernel/trace/ring_buffer.c | 25 ++-
> kernel/trace/trace.c | 12 +-
> kernel/trace/trace_dynevent.c | 6 +-
> kernel/trace/trace_dynevent.h | 5 +-
> kernel/trace/trace_events.c | 35 ++--
> kernel/trace/trace_events_filter.c | 4 +-
> kernel/trace/trace_events_hist.c | 8 +-
> kernel/trace/trace_events_trigger.c | 17 +-
> kernel/trace/trace_events_user.c | 16 +-
> kernel/trace/trace_stat.c | 4 +-
> kernel/user-return-notifier.c | 3 +-
> kernel/workqueue.c | 16 +-
> mm/backing-dev.c | 8 +-
> mm/balloon.c | 8 +-
> mm/cma.c | 4 +-
> mm/compaction.c | 4 +-
> mm/damon/core.c | 4 +-
> mm/damon/sysfs-schemes.c | 4 +-
> mm/dmapool.c | 4 +-
> mm/huge_memory.c | 8 +-
> mm/hugetlb.c | 56 +++---
> mm/hugetlb_vmemmap.c | 16 +-
> mm/khugepaged.c | 14 +-
> mm/kmemleak.c | 7 +-
> mm/ksm.c | 25 +--
> mm/list_lru.c | 4 +-
> mm/memcontrol-v1.c | 8 +-
> mm/memory-failure.c | 12 +-
> mm/memory-tiers.c | 4 +-
> mm/migrate.c | 23 ++-
> mm/mmu_notifier.c | 9 +-
> mm/page_alloc.c | 8 +-
> mm/page_reporting.c | 2 +-
> mm/percpu.c | 11 +-
> mm/pgtable-generic.c | 4 +-
> mm/rmap.c | 10 +-
> mm/shmem.c | 9 +-
> mm/slab_common.c | 14 +-
> mm/slub.c | 33 ++--
> mm/swapfile.c | 4 +-
> mm/userfaultfd.c | 12 +-
> mm/vmalloc.c | 24 +--
> mm/vmscan.c | 7 +-
> mm/zsmalloc.c | 4 +-
> 124 files changed, 875 insertions(+), 681 deletions(-)
Not sure what you were thinking, but this diff stat
is not landable.
pw-bot: cr
^ permalink raw reply
* Re: [PATCH v9 3/6] mm: memcontrol: add interface for swap tier selection
From: Youngjun Park @ 2026-06-22 5:03 UTC (permalink / raw)
To: Youngjun Park
Cc: akpm, chrisl, linux-mm, cgroups, linux-kernel, kasong, hannes,
mhocko, roman.gushchin, shakeel.butt, muchun.song, shikemeng,
nphamcs, baoquan.he, baohua, yosry, gunho.lee, taejoon.song,
hyungjun.cho, mkoutny, baver.bae, matia.kim
In-Reply-To: <20260620181635.299364-4-youngjun.park@lge.com>
https://sashiko.dev/#/patchset/20260620181635.299364-1-youngjun.park@lge.com?part=3
Regarding the Sashiko review comment, I don't think this needs to be
fixed. It is acceptable for the effective_mask to be stale on a zombie
memcg.
The zombie memcg LRU is already reparented by the following patchset.
https://lore.kernel.org/all/cover.1772711148.git.zhengqi.arch@bytedance.com/
the effective_mask is only needed on the swap path. Since
swap I/O rarely (if ever) happens on a zombie memcg, leaving the stale
mask should not cause issues.
(As I mentioned above, there is no need to fix as I think.
But somehow if we needed to fix this, there would be two alternatives.
1. Fix it at offline time by setting it to SWAP_TIER_ALL_MASK. However,
this approach would break the parent tier relationship.
2. Change the memcg iteration routine to use css_for_each_descendant_pre
to explicitly handle the zombie memcg case. This approach would
safely preserve the parent tier relationship.
)
^ permalink raw reply
* [tj-cgroup:test-merge-for-7.2] BUILD SUCCESS b62299084fb88382321b5eed2edef2a0d0cdf117
From: kernel test robot @ 2026-06-22 5:54 UTC (permalink / raw)
To: Tejun Heo; +Cc: cgroups
tree/branch: https://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup.git test-merge-for-7.2
branch HEAD: b62299084fb88382321b5eed2edef2a0d0cdf117 Merge branch 'for-7.2' into test-merge-for-7.2
elapsed time: 6339m
configs tested: 230
configs skipped: 3
The following configs have been built successfully.
More configs may be tested in the coming days.
tested configs:
alpha allnoconfig gcc-16.1.0
alpha allyesconfig gcc-16.1.0
alpha defconfig gcc-16.1.0
arc allmodconfig clang-23
arc allmodconfig gcc-16.1.0
arc allnoconfig gcc-16.1.0
arc allyesconfig clang-23
arc allyesconfig gcc-16.1.0
arc defconfig gcc-16.1.0
arc randconfig-001-20260618 gcc-15.2.0
arc randconfig-002-20260618 gcc-15.2.0
arc randconfig-002-20260618 gcc-8.5.0
arm allnoconfig clang-17
arm allnoconfig gcc-16.1.0
arm allyesconfig clang-23
arm allyesconfig gcc-16.1.0
arm defconfig gcc-16.1.0
arm pxa3xx_defconfig clang-17
arm randconfig-001-20260618 gcc-10.5.0
arm randconfig-001-20260618 gcc-15.2.0
arm randconfig-002-20260618 clang-17
arm randconfig-002-20260618 gcc-15.2.0
arm randconfig-003-20260618 gcc-13.4.0
arm randconfig-003-20260618 gcc-15.2.0
arm randconfig-004-20260618 gcc-15.2.0
arm64 allmodconfig clang-23
arm64 allnoconfig gcc-16.1.0
arm64 defconfig gcc-16.1.0
arm64 randconfig-001-20260618 gcc-15.2.0
arm64 randconfig-002-20260618 gcc-15.2.0
arm64 randconfig-003-20260618 gcc-15.2.0
arm64 randconfig-004-20260618 gcc-15.2.0
csky allmodconfig gcc-16.1.0
csky allnoconfig gcc-16.1.0
csky defconfig gcc-16.1.0
csky randconfig-001-20260618 gcc-15.2.0
csky randconfig-002-20260618 gcc-15.2.0
hexagon allmodconfig clang-23
hexagon allmodconfig gcc-16.1.0
hexagon allnoconfig clang-23
hexagon allnoconfig gcc-16.1.0
hexagon defconfig gcc-16.1.0
hexagon randconfig-001-20260618 clang-17
hexagon randconfig-001-20260618 clang-23
hexagon randconfig-002-20260618 clang-23
i386 allmodconfig clang-22
i386 allmodconfig gcc-14
i386 allnoconfig gcc-14
i386 allnoconfig gcc-16.1.0
i386 allyesconfig clang-22
i386 buildonly-randconfig-001-20260618 gcc-14
i386 buildonly-randconfig-002-20260618 gcc-14
i386 buildonly-randconfig-003-20260618 gcc-14
i386 buildonly-randconfig-004-20260618 gcc-14
i386 buildonly-randconfig-005-20260618 gcc-14
i386 buildonly-randconfig-006-20260618 gcc-14
i386 defconfig gcc-16.1.0
i386 randconfig-001-20260618 clang-22
i386 randconfig-002-20260618 clang-22
i386 randconfig-003-20260618 clang-22
i386 randconfig-004-20260618 clang-22
i386 randconfig-005-20260618 clang-22
i386 randconfig-006-20260618 clang-22
i386 randconfig-007-20260618 clang-22
i386 randconfig-011-20260618 clang-22
i386 randconfig-012-20260618 clang-22
i386 randconfig-013-20260618 clang-22
i386 randconfig-014-20260618 clang-22
i386 randconfig-014-20260618 gcc-14
i386 randconfig-015-20260618 clang-22
i386 randconfig-016-20260618 clang-22
i386 randconfig-017-20260618 clang-22
loongarch allmodconfig clang-19
loongarch allmodconfig clang-23
loongarch allnoconfig clang-20
loongarch allnoconfig gcc-16.1.0
loongarch defconfig clang-23
loongarch randconfig-001-20260618 clang-23
loongarch randconfig-002-20260618 clang-23
loongarch randconfig-002-20260618 gcc-16.1.0
m68k allmodconfig gcc-16.1.0
m68k allnoconfig gcc-16.1.0
m68k allyesconfig clang-23
m68k allyesconfig gcc-16.1.0
m68k defconfig clang-23
m68k defconfig gcc-16.1.0
microblaze allnoconfig gcc-16.1.0
microblaze allyesconfig gcc-16.1.0
microblaze defconfig clang-23
microblaze defconfig gcc-16.1.0
mips allmodconfig gcc-16.1.0
mips allnoconfig gcc-16.1.0
mips allyesconfig gcc-16.1.0
mips cobalt_defconfig gcc-16.1.0
nios2 allmodconfig clang-20
nios2 allmodconfig gcc-11.5.0
nios2 allnoconfig clang-23
nios2 allnoconfig gcc-11.5.0
nios2 defconfig clang-23
nios2 defconfig gcc-11.5.0
nios2 randconfig-001-20260618 clang-23
nios2 randconfig-001-20260618 gcc-8.5.0
nios2 randconfig-002-20260618 clang-23
nios2 randconfig-002-20260618 gcc-11.5.0
openrisc allmodconfig clang-20
openrisc allmodconfig gcc-16.1.0
openrisc allnoconfig clang-23
openrisc allnoconfig gcc-16.1.0
openrisc defconfig gcc-16.1.0
parisc allmodconfig gcc-16.1.0
parisc allnoconfig clang-23
parisc allnoconfig gcc-16.1.0
parisc allyesconfig clang-17
parisc allyesconfig gcc-16.1.0
parisc defconfig gcc-16.1.0
parisc randconfig-001-20260618 gcc-14.3.0
parisc randconfig-001-20260618 gcc-16.1.0
parisc randconfig-002-20260618 gcc-16.1.0
parisc64 defconfig clang-23
parisc64 defconfig gcc-16.1.0
powerpc allmodconfig gcc-16.1.0
powerpc allnoconfig clang-23
powerpc allnoconfig gcc-16.1.0
powerpc randconfig-001-20260618 gcc-16.1.0
powerpc randconfig-001-20260618 gcc-8.5.0
powerpc randconfig-002-20260618 gcc-11.5.0
powerpc randconfig-002-20260618 gcc-16.1.0
powerpc64 randconfig-001-20260618 gcc-16.1.0
powerpc64 randconfig-001-20260618 gcc-8.5.0
powerpc64 randconfig-002-20260618 clang-17
powerpc64 randconfig-002-20260618 gcc-16.1.0
riscv allmodconfig clang-23
riscv allnoconfig clang-23
riscv allnoconfig gcc-16.1.0
riscv allyesconfig clang-23
riscv defconfig gcc-16.1.0
riscv randconfig-001-20260618 gcc-11.5.0
riscv randconfig-001-20260618 gcc-13.4.0
riscv randconfig-002-20260618 clang-20
riscv randconfig-002-20260618 gcc-13.4.0
s390 allmodconfig clang-17
s390 allmodconfig clang-23
s390 allnoconfig clang-23
s390 allyesconfig gcc-16.1.0
s390 defconfig gcc-16.1.0
s390 randconfig-001-20260618 clang-23
s390 randconfig-001-20260618 gcc-13.4.0
s390 randconfig-002-20260618 clang-23
s390 randconfig-002-20260618 gcc-13.4.0
sh allmodconfig gcc-16.1.0
sh allnoconfig clang-23
sh allnoconfig gcc-16.1.0
sh allyesconfig clang-17
sh allyesconfig gcc-16.1.0
sh ap325rxa_defconfig gcc-16.1.0
sh defconfig gcc-14
sh defconfig gcc-16.1.0
sh edosk7760_defconfig gcc-16.1.0
sh randconfig-001-20260618 gcc-13.4.0
sh randconfig-002-20260618 gcc-13.4.0
sh randconfig-002-20260618 gcc-16.1.0
sh se7619_defconfig gcc-16.1.0
sparc allnoconfig clang-23
sparc allnoconfig gcc-16.1.0
sparc defconfig gcc-16.1.0
sparc randconfig-001-20260618 gcc-14.3.0
sparc randconfig-002-20260618 gcc-14.3.0
sparc64 allmodconfig clang-20
sparc64 defconfig clang-23
sparc64 defconfig gcc-14
sparc64 randconfig-001-20260618 gcc-14.3.0
sparc64 randconfig-002-20260618 gcc-14.3.0
um allmodconfig clang-17
um allnoconfig clang-17
um allnoconfig clang-23
um allyesconfig gcc-14
um allyesconfig gcc-16.1.0
um defconfig clang-23
um defconfig gcc-14
um i386_defconfig gcc-14
um randconfig-001-20260618 gcc-14.3.0
um randconfig-002-20260618 gcc-14.3.0
um x86_64_defconfig clang-23
um x86_64_defconfig gcc-14
x86_64 allmodconfig clang-22
x86_64 allnoconfig clang-22
x86_64 allnoconfig clang-23
x86_64 allyesconfig clang-22
x86_64 buildonly-randconfig-001-20260618 clang-22
x86_64 buildonly-randconfig-002-20260618 clang-22
x86_64 buildonly-randconfig-003-20260618 clang-22
x86_64 buildonly-randconfig-004-20260618 clang-22
x86_64 buildonly-randconfig-005-20260618 clang-22
x86_64 buildonly-randconfig-006-20260618 clang-22
x86_64 defconfig gcc-14
x86_64 kexec clang-22
x86_64 randconfig-001-20260618 clang-22
x86_64 randconfig-002-20260618 clang-22
x86_64 randconfig-002-20260618 gcc-14
x86_64 randconfig-003-20260618 clang-22
x86_64 randconfig-004-20260618 clang-22
x86_64 randconfig-005-20260618 clang-22
x86_64 randconfig-005-20260618 gcc-14
x86_64 randconfig-006-20260618 clang-22
x86_64 randconfig-011-20260618 clang-22
x86_64 randconfig-011-20260618 gcc-14
x86_64 randconfig-012-20260618 gcc-14
x86_64 randconfig-013-20260618 gcc-14
x86_64 randconfig-014-20260618 gcc-14
x86_64 randconfig-015-20260618 clang-22
x86_64 randconfig-015-20260618 gcc-14
x86_64 randconfig-016-20260618 gcc-14
x86_64 randconfig-071-20260618 clang-22
x86_64 randconfig-072-20260618 clang-22
x86_64 randconfig-073-20260618 clang-22
x86_64 randconfig-074-20260618 clang-22
x86_64 randconfig-075-20260618 clang-22
x86_64 randconfig-076-20260618 clang-22
x86_64 rhel-9.4 clang-22
x86_64 rhel-9.4-bpf gcc-14
x86_64 rhel-9.4-func clang-22
x86_64 rhel-9.4-kselftests clang-22
x86_64 rhel-9.4-kunit gcc-14
x86_64 rhel-9.4-ltp gcc-14
x86_64 rhel-9.4-rust clang-22
xtensa allnoconfig clang-23
xtensa allnoconfig gcc-16.1.0
xtensa allyesconfig clang-20
xtensa randconfig-001-20260618 gcc-14.3.0
xtensa randconfig-002-20260618 gcc-14.3.0
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH v3 0/7] Prepare mutable list iterators to cache cursor state
From: Kaitao Cheng @ 2026-06-22 6:15 UTC (permalink / raw)
To: Alexei Starovoitov
Cc: Andrew Morton, David Hildenbrand, Jens Axboe, Tejun Heo,
Alexander Viro, Christian Brauner, Alexei Starovoitov,
Daniel Borkmann, Andrii Nakryiko, Johannes Weiner, Peter Zijlstra,
Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim,
Thomas Gleixner, Juri Lelli, Vincent Guittot, Paul Moore,
Andy Shevchenko, Paul E. McKenney, Shakeel Butt,
Christian König, David Howells, Simona Vetter, Randy Dunlap,
Luca Ceresoli, Philipp Stanner, linux-block, LKML,
open list:CONTROL GROUP (CGROUP), linux-ntfs-dev, Linux-Fsdevel,
io-uring, audit, bpf, Network Development, dri-devel,
linux-perf-use., linux-trace-kernel, kexec, live-patching,
linux-modules, Linux Crypto Mailing List, Linux Power Management,
rcu, sched-ext, linux-mm, virtualization, damon,
clang-built-linux, chengkaitao, Muchun Song
In-Reply-To: <CAADnVQJmPWFT01b7DuLdtafv=8FyB84GYHNZ8zSTck+9Aw0JpA@mail.gmail.com>
在 2026/6/22 13:28, Alexei Starovoitov 写道:
> On Sun, Jun 21, 2026 at 9:06 PM Kaitao Cheng <kaitao.cheng@linux.dev> wrote:
>>
>> From: chengkaitao <chengkaitao@kylinos.cn>
>>
>> The list_for_each*_safe() helpers are used when the loop body may remove
>> the current entry. Their current interface, however, forces every caller
>> to define a temporary cursor outside the macro and pass it in, even when
>> the caller never uses that cursor directly. For most call sites this
>> extra cursor is just boilerplate required by the macro implementation.
>>
>> This is awkward because the saved next pointer is an internal detail of
>> the iteration. Callers that only remove or move the current entry do not
>> need to spell it out.
>>
>> The _safe() suffix has also caused confusion. Christian Koenig pointed
>> out that the name is easy to read as a thread-safe variant, especially
>> for beginners, even though it only means that the iterator keeps enough
>> state to tolerate removal of the current entry. He suggested _mutable()
>> as a clearer description of what the loop permits.
>>
>> Add *_mutable() iterator variants for list, hlist and llist. The new
>> helpers are variadic and support both forms. In the common case, the
>> caller omits the temporary cursor and the macro creates a unique internal
>> cursor with typeof(pos) and __UNIQUE_ID(). If a loop really needs an
>> explicit temporary cursor, the caller can still pass it and the helper
>> keeps the existing *_safe() behaviour.
>>
>> For example, a call site may use the shorter form:
>>
>> list_for_each_entry_mutable(pos, head, member)
>>
>> or keep the explicit temporary cursor form:
>>
>> list_for_each_entry_mutable(pos, tmp, head, member)
>>
>> The existing *_safe() helpers remain available for compatibility. This
>> series only converts users in mm, block, kernel, init and io_uring. If
>> this approach looks acceptable, the remaining users can be converted in
>> follow-up series.
>>
>> Changes in v3 (Christian König, Andy Shevchenko):
>> - Convert safe list walks to mutable iterators
>>
>> Changes in v2 (Muchun Song, Andy Shevchenko):
>> - Drop the list_for_each_entry_mutable*() helpers from v1 and make the
>> cursor change directly in the existing list_for_each_entry*() helpers.
>> - Open-code special list walks that rely on updating the loop cursor in
>> the body, preserving their existing traversal semantics.
>>
>> Link to v2:
>> https://lore.kernel.org/all/20260609061347.93688-1-kaitao.cheng@linux.dev/
>>
>> Link to v1:
>> https://lore.kernel.org/all/20260529082149.76764-1-kaitao.cheng@linux.dev/
>>
>> Kaitao Cheng (7):
>> list: Add mutable iterator variants
>> llist: Add mutable iterator variants
>> mm: Use mutable list iterators
>> block: Use mutable list iterators
>> kernel: Use mutable list iterators
>> initramfs: Use mutable list iterator
>> io_uring: Use mutable list iterators
>>
>> block/bfq-iosched.c | 17 +-
>> block/blk-cgroup.c | 12 +-
>> block/blk-flush.c | 4 +-
>> block/blk-iocost.c | 18 +-
>> block/blk-mq.c | 8 +-
>> block/blk-throttle.c | 4 +-
>> block/kyber-iosched.c | 4 +-
>> block/partitions/ldm.c | 8 +-
>> block/sed-opal.c | 4 +-
>> include/linux/list.h | 269 ++++++++++++++++++++++++----
>> include/linux/llist.h | 81 +++++++--
>> init/initramfs.c | 5 +-
>> io_uring/cancel.c | 6 +-
>> io_uring/poll.c | 3 +-
>> io_uring/rw.c | 4 +-
>> io_uring/timeout.c | 8 +-
>> io_uring/uring_cmd.c | 3 +-
>> kernel/audit_tree.c | 4 +-
>> kernel/audit_watch.c | 16 +-
>> kernel/auditfilter.c | 4 +-
>> kernel/auditsc.c | 4 +-
>> kernel/bpf/arena.c | 10 +-
>> kernel/bpf/arraymap.c | 8 +-
>> kernel/bpf/bpf_local_storage.c | 3 +-
>> kernel/bpf/bpf_lru_list.c | 25 ++-
>> kernel/bpf/btf.c | 18 +-
>> kernel/bpf/cgroup.c | 7 +-
>> kernel/bpf/cpumap.c | 4 +-
>> kernel/bpf/devmap.c | 10 +-
>> kernel/bpf/helpers.c | 8 +-
>> kernel/bpf/local_storage.c | 4 +-
>> kernel/bpf/memalloc.c | 16 +-
>> kernel/bpf/offload.c | 8 +-
>> kernel/bpf/states.c | 4 +-
>> kernel/bpf/stream.c | 4 +-
>> kernel/bpf/verifier.c | 6 +-
>> kernel/cgroup/cgroup-v1.c | 4 +-
>> kernel/cgroup/cgroup.c | 54 +++---
>> kernel/cgroup/dmem.c | 12 +-
>> kernel/cgroup/rdma.c | 8 +-
>> kernel/events/core.c | 44 +++--
>> kernel/events/uprobes.c | 12 +-
>> kernel/exit.c | 8 +-
>> kernel/fail_function.c | 4 +-
>> kernel/gcov/clang.c | 4 +-
>> kernel/irq_work.c | 4 +-
>> kernel/kexec_core.c | 4 +-
>> kernel/kprobes.c | 16 +-
>> kernel/livepatch/core.c | 4 +-
>> kernel/livepatch/core.h | 4 +-
>> kernel/liveupdate/kho_block.c | 4 +-
>> kernel/liveupdate/luo_flb.c | 4 +-
>> kernel/locking/rwsem.c | 2 +-
>> kernel/locking/test-ww_mutex.c | 2 +-
>> kernel/module/main.c | 11 +-
>> kernel/padata.c | 4 +-
>> kernel/power/snapshot.c | 8 +-
>> kernel/power/wakelock.c | 4 +-
>> kernel/printk/printk.c | 11 +-
>> kernel/ptrace.c | 4 +-
>> kernel/rcu/rcutorture.c | 3 +-
>> kernel/rcu/tasks.h | 9 +-
>> kernel/rcu/tree.c | 6 +-
>> kernel/resource.c | 4 +-
>> kernel/sched/core.c | 4 +-
>> kernel/sched/ext.c | 22 +--
>> kernel/sched/fair.c | 28 +--
>> kernel/sched/topology.c | 4 +-
>> kernel/sched/wait.c | 4 +-
>> kernel/seccomp.c | 4 +-
>> kernel/signal.c | 11 +-
>> kernel/smp.c | 4 +-
>> kernel/taskstats.c | 8 +-
>> kernel/time/clockevents.c | 6 +-
>> kernel/time/clocksource.c | 4 +-
>> kernel/time/posix-cpu-timers.c | 4 +-
>> kernel/time/posix-timers.c | 3 +-
>> kernel/torture.c | 3 +-
>> kernel/trace/bpf_trace.c | 4 +-
>> kernel/trace/ftrace.c | 49 +++--
>> kernel/trace/ring_buffer.c | 25 ++-
>> kernel/trace/trace.c | 12 +-
>> kernel/trace/trace_dynevent.c | 6 +-
>> kernel/trace/trace_dynevent.h | 5 +-
>> kernel/trace/trace_events.c | 35 ++--
>> kernel/trace/trace_events_filter.c | 4 +-
>> kernel/trace/trace_events_hist.c | 8 +-
>> kernel/trace/trace_events_trigger.c | 17 +-
>> kernel/trace/trace_events_user.c | 16 +-
>> kernel/trace/trace_stat.c | 4 +-
>> kernel/user-return-notifier.c | 3 +-
>> kernel/workqueue.c | 16 +-
>> mm/backing-dev.c | 8 +-
>> mm/balloon.c | 8 +-
>> mm/cma.c | 4 +-
>> mm/compaction.c | 4 +-
>> mm/damon/core.c | 4 +-
>> mm/damon/sysfs-schemes.c | 4 +-
>> mm/dmapool.c | 4 +-
>> mm/huge_memory.c | 8 +-
>> mm/hugetlb.c | 56 +++---
>> mm/hugetlb_vmemmap.c | 16 +-
>> mm/khugepaged.c | 14 +-
>> mm/kmemleak.c | 7 +-
>> mm/ksm.c | 25 +--
>> mm/list_lru.c | 4 +-
>> mm/memcontrol-v1.c | 8 +-
>> mm/memory-failure.c | 12 +-
>> mm/memory-tiers.c | 4 +-
>> mm/migrate.c | 23 ++-
>> mm/mmu_notifier.c | 9 +-
>> mm/page_alloc.c | 8 +-
>> mm/page_reporting.c | 2 +-
>> mm/percpu.c | 11 +-
>> mm/pgtable-generic.c | 4 +-
>> mm/rmap.c | 10 +-
>> mm/shmem.c | 9 +-
>> mm/slab_common.c | 14 +-
>> mm/slub.c | 33 ++--
>> mm/swapfile.c | 4 +-
>> mm/userfaultfd.c | 12 +-
>> mm/vmalloc.c | 24 +--
>> mm/vmscan.c | 7 +-
>> mm/zsmalloc.c | 4 +-
>> 124 files changed, 875 insertions(+), 681 deletions(-)
>
> Not sure what you were thinking, but this diff stat
> is not landable.
[PATCH v3 1/7] and [PATCH v3 2/7] contain the main logic and can
be merged directly. They are also compatible with the old API.
[PATCH v3 3/7] through [PATCH v3 7/7] are just simple interface
replacements and do not change any functional logic. They can be
left unmerged for now; individual modules can pick them up later
if needed.
In v2, Andy Shevchenko mentioned: "If it's done by Linus himself
during the day when he prepares -rc1, it's fine." Even so, the
changes in this patch series are indeed quite large and touch
almost every subsystem. I have only converted part of them for
now, so I wanted to send this out first and see what people think.
--
Thanks
Kaitao Cheng
^ permalink raw reply
* Re: [PATCH v2] cgroup/cpuset: rebind mm mempolicy to effective_mems, not mems_allowed
From: David Hildenbrand (Arm) @ 2026-06-22 7:12 UTC (permalink / raw)
To: Waiman Long, Gregory Price
Cc: Farhad Alemi, Andrew Morton, Farhad Alemi, Yury Norov,
Joshua Hahn, Zi Yan, Matthew Brost, Rakie Kim, Byungchul Park,
Ying Huang, Alistair Popple, Rasmus Villemoes, linux-mm,
linux-kernel, cgroups, stable
In-Reply-To: <9d8b650d-6faa-425a-8db7-1e206cb25158@redhat.com>
On 6/21/26 05:24, Waiman Long wrote:
> On 6/18/26 4:41 AM, David Hildenbrand (Arm) wrote:
>> On 6/16/26 17:23, Waiman Long wrote:
>>> Yes, taking newmems is a reasonable choice and there are pros and cons with each
>>> options. My focus is more on not changing how v1 cpuset behaves as it is well
>>> defined in the v1 cpusets.rst file:
>>>
>>> Requests by a task, using the sched_setaffinity(2) system call to
>>> include CPUs in its CPU affinity mask, and using the mbind(2) and
>>> set_mempolicy(2) system calls to include Memory Nodes in its memory
>>> policy, are both filtered through that task's cpuset, filtering out any
>>> CPUs or Memory Nodes not in that cpuset. The scheduler will not
>>> schedule a task on a CPU that is not allowed in its cpus_allowed
>>> vector, and the kernel page allocator will not allocate a page on a
>>> node that is not allowed in the requesting task's mems_allowed vector.
>>>
>>> v2, OTOH, is more vague as to what setting cpuset.mems will mean and we
>>> generally follow what v1 is doing, but we have more leeway of what we can do.
>>>
>>> Using newmems will make the above text not totally correct. At least the offline
>>> memory nodes will be filtered out which will not be utilized by the task when
>>> the offline node becomes online. That is why I am saying that we will have to
>>> correct the documentation if we want to make this change.
>> So IIUC:
>>
>> diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
>> index 1335e437098e..cdfc615f35a5 100644
>> --- a/kernel/cgroup/cpuset.c
>> +++ b/kernel/cgroup/cpuset.c
>> @@ -2645,7 +2645,13 @@ void cpuset_update_tasks_nodemask(struct cpuset *cs)
>> migrate = is_memory_migrate(cs);
>> - mpol_rebind_mm(mm, &cs->mems_allowed);
>> + /*
>> + * For v1 we can have empty effective_mems, but we cannot
>> + * attach any tasks (see cpuset_can_attach_check()). For v2,
>> + * it's guaranteed to not be empty.
>> + */
>> + VM_WARN_ON_ONCE(nodes_empty(cs->effective_mems));
>> + mpol_rebind_mm(mm, &cs->effective_mems);
>> if (migrate)
>> cpuset_migrate_mm(mm, &cs->old_mems_allowed, &newmems);
>> else
>
> That is true, but I don't think we need a VM_WARN_ON_ONCE() here.
I'd prefer if we catch such stuff in the future more easily than running into
late divide-by-zero. Maybe we should check in mpol_rebind_mm() instead.
--
Cheers,
David
^ permalink raw reply
* [PATCH 1/2] blk-cgroup: fix blkg leak in blkg_create() error path
From: Zizhi Wo @ 2026-06-22 7:07 UTC (permalink / raw)
To: axboe, tj, josef, linux-block
Cc: cgroups, yangerkun, chengzhihao1, houtao1, yukuai, wozizhi
In-Reply-To: <20260622070714.1158886-1-wozizhi@huaweicloud.com>
When radix_tree_insert() fails in blkg_create(), the error path calls
blkg_put() to release the blkg. This was correct when blkg->refcnt was an
atomic_t: blkg_put() dropped it to 0 and triggered the release path.
But commit 7fcf2b033b84 ("blkcg: change blkg reference counting to use
percpu_ref") switched refcnt to a percpu_ref. In percpu mode
percpu_ref_put() never checks for zero, so the release callback is never
invoked. This blkg is on neither blkcg->blkg_list nor queue->blkg_list, so
blkg_destroy_all() / blkcg_destroy_blkgs() can never reach it to call
blkg_destroy()->percpu_ref_kill() either, cause the leak.
Fix it by killing the percpu_ref instead, which switches it to atomic mode
and drops the initial ref.
Fixes: 7fcf2b033b84 ("blkcg: change blkg reference counting to use percpu_ref")
Signed-off-by: Zizhi Wo <wozizhi@huaweicloud.com>
Signed-off-by: Zizhi Wo <wozizhi@huawei.com>
---
block/blk-cgroup.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c
index bc63bd220865..6386fe413994 100644
--- a/block/blk-cgroup.c
+++ b/block/blk-cgroup.c
@@ -437,11 +437,11 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, struct gendisk *disk,
if (!ret)
return blkg;
/* @blkg failed fully initialized, use the usual release path */
- blkg_put(blkg);
+ percpu_ref_kill(&blkg->refcnt);
return ERR_PTR(ret);
err_put_css:
css_put(&blkcg->css);
err_free_blkg:
--
2.52.0
^ permalink raw reply related
* [PATCH 0/2] fix two issues in blkg_create() error path
From: Zizhi Wo @ 2026-06-22 7:07 UTC (permalink / raw)
To: axboe, tj, josef, linux-block
Cc: cgroups, yangerkun, chengzhihao1, houtao1, yukuai, wozizhi
From: Zizhi Wo <wozizhi@huawei.com>
This series fixes two issues on the blkg_create() error path.
Patch 1 fixes a blkg leak when blkg_create() fails.
Patch 2 fixes a null-ptr-deref by freeing blkg->pd synchronously on the
error path, otherwise the async free path may dereference an already
unregistered blkcg_policy.
Zizhi Wo (2):
blk-cgroup: fix blkg leak in blkg_create() error path
blk-cgroup: fix Null-ptr-deref by freeing blkg pd on blkg_create error
path
block/blk-cgroup.c | 32 +++++++++++++++++++++++++-------
1 file changed, 25 insertions(+), 7 deletions(-)
--
2.52.0
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox