In order for the task_struct to hold references to rdtgroups, it must be
possible to release these references before a concurrent deletion causes
them to be freed.
It is not possible for resctrl code to do this with
for_each_process_thread() because the task can still switch in after it
has been removed from the tasklist, at which point the task_struct could
be referring to freed memory.
Signed-off-by: Peter Newman <[email protected]>
---
arch/x86/kernel/cpu/resctrl/rdtgroup.c | 10 ++++++++++
include/linux/resctrl.h | 6 ++++++
kernel/exit.c | 3 +++
3 files changed, 19 insertions(+)
diff --git a/arch/x86/kernel/cpu/resctrl/rdtgroup.c b/arch/x86/kernel/cpu/resctrl/rdtgroup.c
index 5d599d99f94b..9b1969e4235a 100644
--- a/arch/x86/kernel/cpu/resctrl/rdtgroup.c
+++ b/arch/x86/kernel/cpu/resctrl/rdtgroup.c
@@ -2931,6 +2931,16 @@ static void rdt_move_group_tasks(struct rdtgroup *from, struct rdtgroup *to,
read_unlock(&tasklist_lock);
}
+/**
+ * exit_resctrl() - called at thread destruction to release resources
+ *
+ * This hook is called just before the task is removed from the global tasklist
+ * and still reachable via for_each_process_thread().
+ */
+void exit_resctrl(struct task_struct *tsk)
+{
+}
+
static void free_all_child_rdtgrp(struct rdtgroup *rdtgrp)
{
struct rdtgroup *sentry, *stmp;
diff --git a/include/linux/resctrl.h b/include/linux/resctrl.h
index 62d607939a73..b2af1fbc7aa1 100644
--- a/include/linux/resctrl.h
+++ b/include/linux/resctrl.h
@@ -325,4 +325,10 @@ static inline void resctrl_sched_in(struct task_struct *tsk)
#endif
}
+#ifdef CONFIG_X86_CPU_RESCTRL
+void exit_resctrl(struct task_struct *tsk);
+#else
+static inline void exit_resctrl(struct task_struct *tsk) {}
+#endif
+
#endif /* _RESCTRL_H */
diff --git a/kernel/exit.c b/kernel/exit.c
index 41a12630cbbc..ccdc90ff6d71 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -70,6 +70,7 @@
#include <linux/sysfs.h>
#include <linux/user_events.h>
#include <linux/uaccess.h>
+#include <linux/resctrl.h>
#include <uapi/linux/wait.h>
@@ -862,6 +863,8 @@ void __noreturn do_exit(long code)
tsk->exit_code = code;
taskstats_exit(tsk, group_dead);
+ exit_resctrl(tsk);
+
exit_mm();
if (group_dead)
--
2.44.0.396.g6e790dbe36-goog
Hi Peter,
On 3/25/2024 10:27 AM, Peter Newman wrote:
> In order for the task_struct to hold references to rdtgroups, it must be
> possible to release these references before a concurrent deletion causes
> them to be freed.
This is very hard to parse (for me). I found your description in the
cover letter to be much easier to understand.
Considering the core area of code changed with this patch the
changelog needs to be specific on what is needed and why.
I'd also like to recommend that the subject prefix is changed to "exit: ..."
to highlight to folks that this crosses into a different area of the kernel.
It is not obvious to me how changes are routed to this exit code but we
can start by highlighting this to not appear to want to sneak it in.
>
> It is not possible for resctrl code to do this with
> for_each_process_thread() because the task can still switch in after it
> has been removed from the tasklist, at which point the task_struct could
> be referring to freed memory.
>
> Signed-off-by: Peter Newman <[email protected]>
> ---
> arch/x86/kernel/cpu/resctrl/rdtgroup.c | 10 ++++++++++
> include/linux/resctrl.h | 6 ++++++
> kernel/exit.c | 3 +++
> 3 files changed, 19 insertions(+)
>
> diff --git a/arch/x86/kernel/cpu/resctrl/rdtgroup.c b/arch/x86/kernel/cpu/resctrl/rdtgroup.c
> index 5d599d99f94b..9b1969e4235a 100644
> --- a/arch/x86/kernel/cpu/resctrl/rdtgroup.c
> +++ b/arch/x86/kernel/cpu/resctrl/rdtgroup.c
> @@ -2931,6 +2931,16 @@ static void rdt_move_group_tasks(struct rdtgroup *from, struct rdtgroup *to,
> read_unlock(&tasklist_lock);
> }
>
> +/**
> + * exit_resctrl() - called at thread destruction to release resources
> + *
> + * This hook is called just before the task is removed from the global tasklist
> + * and still reachable via for_each_process_thread().
> + */
> +void exit_resctrl(struct task_struct *tsk)
> +{
> +}
> +
> static void free_all_child_rdtgrp(struct rdtgroup *rdtgrp)
> {
> struct rdtgroup *sentry, *stmp;
> diff --git a/include/linux/resctrl.h b/include/linux/resctrl.h
> index 62d607939a73..b2af1fbc7aa1 100644
> --- a/include/linux/resctrl.h
> +++ b/include/linux/resctrl.h
> @@ -325,4 +325,10 @@ static inline void resctrl_sched_in(struct task_struct *tsk)
> #endif
> }
>
> +#ifdef CONFIG_X86_CPU_RESCTRL
> +void exit_resctrl(struct task_struct *tsk);
> +#else
> +static inline void exit_resctrl(struct task_struct *tsk) {}
> +#endif
Scattering these ifdefs in the header file is not ideal. I think when there
are just the two sections as I mentioned in previous patch this will look better.
> +
> #endif /* _RESCTRL_H */
> diff --git a/kernel/exit.c b/kernel/exit.c
> index 41a12630cbbc..ccdc90ff6d71 100644
> --- a/kernel/exit.c
> +++ b/kernel/exit.c
> @@ -70,6 +70,7 @@
> #include <linux/sysfs.h>
> #include <linux/user_events.h>
> #include <linux/uaccess.h>
> +#include <linux/resctrl.h>
>
> #include <uapi/linux/wait.h>
>
> @@ -862,6 +863,8 @@ void __noreturn do_exit(long code)
> tsk->exit_code = code;
> taskstats_exit(tsk, group_dead);
>
> + exit_resctrl(tsk);
> +
This seems fair but I am not familiar with the customs in this area of the kernel.
I see both exit_xxx() and xxx_task_exit() being used.
Reinette