mirror of
https://github.com/tbsdtv/linux_media.git
synced 2025-07-23 04:33:26 +02:00
Pull io_uring thread rewrite from Jens Axboe: "This converts the io-wq workers to be forked off the tasks in question instead of being kernel threads that assume various bits of the original task identity. This kills > 400 lines of code from io_uring/io-wq, and it's the worst part of the code. We've had several bugs in this area, and the worry is always that we could be missing some pieces for file types doing unusual things (recent /dev/tty example comes to mind, userfaultfd reads installing file descriptors is another fun one... - both of which need special handling, and I bet it's not the last weird oddity we'll find). With these identical workers, we can have full confidence that we're never missing anything. That, in itself, is a huge win. Outside of that, it's also more efficient since we're not wasting space and code on tracking state, or switching between different states. I'm sure we're going to find little things to patch up after this series, but testing has been pretty thorough, from the usual regression suite to production. Any issue that may crop up should be manageable. There's also a nice series of further reductions we can do on top of this, but I wanted to get the meat of it out sooner rather than later. The general worry here isn't that it's fundamentally broken. Most of the little issues we've found over the last week have been related to just changes in how thread startup/exit is done, since that's the main difference between using kthreads and these kinds of threads. In fact, if all goes according to plan, I want to get this into the 5.10 and 5.11 stable branches as well. That said, the changes outside of io_uring/io-wq are: - arch setup, simple one-liner to each arch copy_thread() implementation. - Removal of net and proc restrictions for io_uring, they are no longer needed or useful" * tag 'io_uring-worker.v3-2021-02-25' of git://git.kernel.dk/linux-block: (30 commits) io-wq: remove now unused IO_WQ_BIT_ERROR io_uring: fix SQPOLL thread handling over exec io-wq: improve manager/worker handling over exec io_uring: ensure SQPOLL startup is triggered before error shutdown io-wq: make buffered file write hashed work map per-ctx io-wq: fix race around io_worker grabbing io-wq: fix races around manager/worker creation and task exit io_uring: ensure io-wq context is always destroyed for tasks arch: ensure parisc/powerpc handle PF_IO_WORKER in copy_thread() io_uring: cleanup ->user usage io-wq: remove nr_process accounting io_uring: flag new native workers with IORING_FEAT_NATIVE_WORKERS net: remove cmsg restriction from io_uring based send/recvmsg calls Revert "proc: don't allow async path resolution of /proc/self components" Revert "proc: don't allow async path resolution of /proc/thread-self components" io_uring: move SQPOLL thread io-wq forked worker io-wq: make io_wq_fork_thread() available to other users io-wq: only remove worker from free_list, if it was there io_uring: remove io_identity io_uring: remove any grabbing of context ...
414 lines
11 KiB
C
414 lines
11 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* linux/arch/sparc/kernel/process.c
|
|
*
|
|
* Copyright (C) 1995, 2008 David S. Miller (davem@davemloft.net)
|
|
* Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
|
|
*/
|
|
|
|
/*
|
|
* This file handles the architecture-dependent parts of process handling..
|
|
*/
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <linux/elfcore.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/module.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/sched/debug.h>
|
|
#include <linux/sched/task.h>
|
|
#include <linux/sched/task_stack.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/stddef.h>
|
|
#include <linux/ptrace.h>
|
|
#include <linux/user.h>
|
|
#include <linux/smp.h>
|
|
#include <linux/reboot.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/pm.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/cpu.h>
|
|
|
|
#include <asm/auxio.h>
|
|
#include <asm/oplib.h>
|
|
#include <linux/uaccess.h>
|
|
#include <asm/page.h>
|
|
#include <asm/delay.h>
|
|
#include <asm/processor.h>
|
|
#include <asm/psr.h>
|
|
#include <asm/elf.h>
|
|
#include <asm/prom.h>
|
|
#include <asm/unistd.h>
|
|
#include <asm/setup.h>
|
|
|
|
#include "kernel.h"
|
|
|
|
/*
|
|
* Power management idle function
|
|
* Set in pm platform drivers (apc.c and pmc.c)
|
|
*/
|
|
void (*sparc_idle)(void);
|
|
|
|
/*
|
|
* Power-off handler instantiation for pm.h compliance
|
|
* This is done via auxio, but could be used as a fallback
|
|
* handler when auxio is not present-- unused for now...
|
|
*/
|
|
void (*pm_power_off)(void) = machine_power_off;
|
|
EXPORT_SYMBOL(pm_power_off);
|
|
|
|
/*
|
|
* sysctl - toggle power-off restriction for serial console
|
|
* systems in machine_power_off()
|
|
*/
|
|
int scons_pwroff = 1;
|
|
|
|
extern void fpsave(unsigned long *, unsigned long *, void *, unsigned long *);
|
|
|
|
struct task_struct *last_task_used_math = NULL;
|
|
struct thread_info *current_set[NR_CPUS];
|
|
|
|
/* Idle loop support. */
|
|
void arch_cpu_idle(void)
|
|
{
|
|
if (sparc_idle)
|
|
(*sparc_idle)();
|
|
raw_local_irq_enable();
|
|
}
|
|
|
|
/* XXX cli/sti -> local_irq_xxx here, check this works once SMP is fixed. */
|
|
void machine_halt(void)
|
|
{
|
|
local_irq_enable();
|
|
mdelay(8);
|
|
local_irq_disable();
|
|
prom_halt();
|
|
panic("Halt failed!");
|
|
}
|
|
|
|
void machine_restart(char * cmd)
|
|
{
|
|
char *p;
|
|
|
|
local_irq_enable();
|
|
mdelay(8);
|
|
local_irq_disable();
|
|
|
|
p = strchr (reboot_command, '\n');
|
|
if (p) *p = 0;
|
|
if (cmd)
|
|
prom_reboot(cmd);
|
|
if (*reboot_command)
|
|
prom_reboot(reboot_command);
|
|
prom_feval ("reset");
|
|
panic("Reboot failed!");
|
|
}
|
|
|
|
void machine_power_off(void)
|
|
{
|
|
if (auxio_power_register &&
|
|
(!of_node_is_type(of_console_device, "serial") || scons_pwroff)) {
|
|
u8 power_register = sbus_readb(auxio_power_register);
|
|
power_register |= AUXIO_POWER_OFF;
|
|
sbus_writeb(power_register, auxio_power_register);
|
|
}
|
|
|
|
machine_halt();
|
|
}
|
|
|
|
void show_regs(struct pt_regs *r)
|
|
{
|
|
struct reg_window32 *rw = (struct reg_window32 *) r->u_regs[14];
|
|
|
|
show_regs_print_info(KERN_DEFAULT);
|
|
|
|
printk("PSR: %08lx PC: %08lx NPC: %08lx Y: %08lx %s\n",
|
|
r->psr, r->pc, r->npc, r->y, print_tainted());
|
|
printk("PC: <%pS>\n", (void *) r->pc);
|
|
printk("%%G: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
|
|
r->u_regs[0], r->u_regs[1], r->u_regs[2], r->u_regs[3],
|
|
r->u_regs[4], r->u_regs[5], r->u_regs[6], r->u_regs[7]);
|
|
printk("%%O: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
|
|
r->u_regs[8], r->u_regs[9], r->u_regs[10], r->u_regs[11],
|
|
r->u_regs[12], r->u_regs[13], r->u_regs[14], r->u_regs[15]);
|
|
printk("RPC: <%pS>\n", (void *) r->u_regs[15]);
|
|
|
|
printk("%%L: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
|
|
rw->locals[0], rw->locals[1], rw->locals[2], rw->locals[3],
|
|
rw->locals[4], rw->locals[5], rw->locals[6], rw->locals[7]);
|
|
printk("%%I: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
|
|
rw->ins[0], rw->ins[1], rw->ins[2], rw->ins[3],
|
|
rw->ins[4], rw->ins[5], rw->ins[6], rw->ins[7]);
|
|
}
|
|
|
|
/*
|
|
* The show_stack() is external API which we do not use ourselves.
|
|
* The oops is printed in die_if_kernel.
|
|
*/
|
|
void show_stack(struct task_struct *tsk, unsigned long *_ksp, const char *loglvl)
|
|
{
|
|
unsigned long pc, fp;
|
|
unsigned long task_base;
|
|
struct reg_window32 *rw;
|
|
int count = 0;
|
|
|
|
if (!tsk)
|
|
tsk = current;
|
|
|
|
if (tsk == current && !_ksp)
|
|
__asm__ __volatile__("mov %%fp, %0" : "=r" (_ksp));
|
|
|
|
task_base = (unsigned long) task_stack_page(tsk);
|
|
fp = (unsigned long) _ksp;
|
|
do {
|
|
/* Bogus frame pointer? */
|
|
if (fp < (task_base + sizeof(struct thread_info)) ||
|
|
fp >= (task_base + (PAGE_SIZE << 1)))
|
|
break;
|
|
rw = (struct reg_window32 *) fp;
|
|
pc = rw->ins[7];
|
|
printk("%s[%08lx : ", loglvl, pc);
|
|
printk("%s%pS ] ", loglvl, (void *) pc);
|
|
fp = rw->ins[6];
|
|
} while (++count < 16);
|
|
printk("%s\n", loglvl);
|
|
}
|
|
|
|
/*
|
|
* Free current thread data structures etc..
|
|
*/
|
|
void exit_thread(struct task_struct *tsk)
|
|
{
|
|
#ifndef CONFIG_SMP
|
|
if (last_task_used_math == tsk) {
|
|
#else
|
|
if (test_tsk_thread_flag(tsk, TIF_USEDFPU)) {
|
|
#endif
|
|
/* Keep process from leaving FPU in a bogon state. */
|
|
put_psr(get_psr() | PSR_EF);
|
|
fpsave(&tsk->thread.float_regs[0], &tsk->thread.fsr,
|
|
&tsk->thread.fpqueue[0], &tsk->thread.fpqdepth);
|
|
#ifndef CONFIG_SMP
|
|
last_task_used_math = NULL;
|
|
#else
|
|
clear_ti_thread_flag(task_thread_info(tsk), TIF_USEDFPU);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void flush_thread(void)
|
|
{
|
|
current_thread_info()->w_saved = 0;
|
|
|
|
#ifndef CONFIG_SMP
|
|
if(last_task_used_math == current) {
|
|
#else
|
|
if (test_thread_flag(TIF_USEDFPU)) {
|
|
#endif
|
|
/* Clean the fpu. */
|
|
put_psr(get_psr() | PSR_EF);
|
|
fpsave(¤t->thread.float_regs[0], ¤t->thread.fsr,
|
|
¤t->thread.fpqueue[0], ¤t->thread.fpqdepth);
|
|
#ifndef CONFIG_SMP
|
|
last_task_used_math = NULL;
|
|
#else
|
|
clear_thread_flag(TIF_USEDFPU);
|
|
#endif
|
|
}
|
|
|
|
/* This task is no longer a kernel thread. */
|
|
if (current->thread.flags & SPARC_FLAG_KTHREAD) {
|
|
current->thread.flags &= ~SPARC_FLAG_KTHREAD;
|
|
|
|
/* We must fixup kregs as well. */
|
|
/* XXX This was not fixed for ti for a while, worked. Unused? */
|
|
current->thread.kregs = (struct pt_regs *)
|
|
(task_stack_page(current) + (THREAD_SIZE - TRACEREG_SZ));
|
|
}
|
|
}
|
|
|
|
static inline struct sparc_stackf __user *
|
|
clone_stackframe(struct sparc_stackf __user *dst,
|
|
struct sparc_stackf __user *src)
|
|
{
|
|
unsigned long size, fp;
|
|
struct sparc_stackf *tmp;
|
|
struct sparc_stackf __user *sp;
|
|
|
|
if (get_user(tmp, &src->fp))
|
|
return NULL;
|
|
|
|
fp = (unsigned long) tmp;
|
|
size = (fp - ((unsigned long) src));
|
|
fp = (unsigned long) dst;
|
|
sp = (struct sparc_stackf __user *)(fp - size);
|
|
|
|
/* do_fork() grabs the parent semaphore, we must release it
|
|
* temporarily so we can build the child clone stack frame
|
|
* without deadlocking.
|
|
*/
|
|
if (__copy_user(sp, src, size))
|
|
sp = NULL;
|
|
else if (put_user(fp, &sp->fp))
|
|
sp = NULL;
|
|
|
|
return sp;
|
|
}
|
|
|
|
/* Copy a Sparc thread. The fork() return value conventions
|
|
* under SunOS are nothing short of bletcherous:
|
|
* Parent --> %o0 == childs pid, %o1 == 0
|
|
* Child --> %o0 == parents pid, %o1 == 1
|
|
*
|
|
* NOTE: We have a separate fork kpsr/kwim because
|
|
* the parent could change these values between
|
|
* sys_fork invocation and when we reach here
|
|
* if the parent should sleep while trying to
|
|
* allocate the task_struct and kernel stack in
|
|
* do_fork().
|
|
* XXX See comment above sys_vfork in sparc64. todo.
|
|
*/
|
|
extern void ret_from_fork(void);
|
|
extern void ret_from_kernel_thread(void);
|
|
|
|
int copy_thread(unsigned long clone_flags, unsigned long sp, unsigned long arg,
|
|
struct task_struct *p, unsigned long tls)
|
|
{
|
|
struct thread_info *ti = task_thread_info(p);
|
|
struct pt_regs *childregs, *regs = current_pt_regs();
|
|
char *new_stack;
|
|
|
|
#ifndef CONFIG_SMP
|
|
if(last_task_used_math == current) {
|
|
#else
|
|
if (test_thread_flag(TIF_USEDFPU)) {
|
|
#endif
|
|
put_psr(get_psr() | PSR_EF);
|
|
fpsave(&p->thread.float_regs[0], &p->thread.fsr,
|
|
&p->thread.fpqueue[0], &p->thread.fpqdepth);
|
|
}
|
|
|
|
/*
|
|
* p->thread_info new_stack childregs stack bottom
|
|
* ! ! ! !
|
|
* V V (stk.fr.) V (pt_regs) V
|
|
* +----- - - - - - ------+===========+=============+
|
|
*/
|
|
new_stack = task_stack_page(p) + THREAD_SIZE;
|
|
new_stack -= STACKFRAME_SZ + TRACEREG_SZ;
|
|
childregs = (struct pt_regs *) (new_stack + STACKFRAME_SZ);
|
|
|
|
/*
|
|
* A new process must start with interrupts disabled, see schedule_tail()
|
|
* and finish_task_switch(). (If we do not do it and if a timer interrupt
|
|
* hits before we unlock and attempts to take the rq->lock, we deadlock.)
|
|
*
|
|
* Thus, kpsr |= PSR_PIL.
|
|
*/
|
|
ti->ksp = (unsigned long) new_stack;
|
|
p->thread.kregs = childregs;
|
|
|
|
if (unlikely(p->flags & (PF_KTHREAD | PF_IO_WORKER))) {
|
|
extern int nwindows;
|
|
unsigned long psr;
|
|
memset(new_stack, 0, STACKFRAME_SZ + TRACEREG_SZ);
|
|
p->thread.flags |= SPARC_FLAG_KTHREAD;
|
|
p->thread.current_ds = KERNEL_DS;
|
|
ti->kpc = (((unsigned long) ret_from_kernel_thread) - 0x8);
|
|
childregs->u_regs[UREG_G1] = sp; /* function */
|
|
childregs->u_regs[UREG_G2] = arg;
|
|
psr = childregs->psr = get_psr();
|
|
ti->kpsr = psr | PSR_PIL;
|
|
ti->kwim = 1 << (((psr & PSR_CWP) + 1) % nwindows);
|
|
return 0;
|
|
}
|
|
memcpy(new_stack, (char *)regs - STACKFRAME_SZ, STACKFRAME_SZ + TRACEREG_SZ);
|
|
childregs->u_regs[UREG_FP] = sp;
|
|
p->thread.flags &= ~SPARC_FLAG_KTHREAD;
|
|
p->thread.current_ds = USER_DS;
|
|
ti->kpc = (((unsigned long) ret_from_fork) - 0x8);
|
|
ti->kpsr = current->thread.fork_kpsr | PSR_PIL;
|
|
ti->kwim = current->thread.fork_kwim;
|
|
|
|
if (sp != regs->u_regs[UREG_FP]) {
|
|
struct sparc_stackf __user *childstack;
|
|
struct sparc_stackf __user *parentstack;
|
|
|
|
/*
|
|
* This is a clone() call with supplied user stack.
|
|
* Set some valid stack frames to give to the child.
|
|
*/
|
|
childstack = (struct sparc_stackf __user *)
|
|
(sp & ~0xfUL);
|
|
parentstack = (struct sparc_stackf __user *)
|
|
regs->u_regs[UREG_FP];
|
|
|
|
#if 0
|
|
printk("clone: parent stack:\n");
|
|
show_stackframe(parentstack);
|
|
#endif
|
|
|
|
childstack = clone_stackframe(childstack, parentstack);
|
|
if (!childstack)
|
|
return -EFAULT;
|
|
|
|
#if 0
|
|
printk("clone: child stack:\n");
|
|
show_stackframe(childstack);
|
|
#endif
|
|
|
|
childregs->u_regs[UREG_FP] = (unsigned long)childstack;
|
|
}
|
|
|
|
#ifdef CONFIG_SMP
|
|
/* FPU must be disabled on SMP. */
|
|
childregs->psr &= ~PSR_EF;
|
|
clear_tsk_thread_flag(p, TIF_USEDFPU);
|
|
#endif
|
|
|
|
/* Set the return value for the child. */
|
|
childregs->u_regs[UREG_I0] = current->pid;
|
|
childregs->u_regs[UREG_I1] = 1;
|
|
|
|
/* Set the return value for the parent. */
|
|
regs->u_regs[UREG_I1] = 0;
|
|
|
|
if (clone_flags & CLONE_SETTLS)
|
|
childregs->u_regs[UREG_G7] = tls;
|
|
|
|
return 0;
|
|
}
|
|
|
|
unsigned long get_wchan(struct task_struct *task)
|
|
{
|
|
unsigned long pc, fp, bias = 0;
|
|
unsigned long task_base = (unsigned long) task;
|
|
unsigned long ret = 0;
|
|
struct reg_window32 *rw;
|
|
int count = 0;
|
|
|
|
if (!task || task == current ||
|
|
task->state == TASK_RUNNING)
|
|
goto out;
|
|
|
|
fp = task_thread_info(task)->ksp + bias;
|
|
do {
|
|
/* Bogus frame pointer? */
|
|
if (fp < (task_base + sizeof(struct thread_info)) ||
|
|
fp >= (task_base + (2 * PAGE_SIZE)))
|
|
break;
|
|
rw = (struct reg_window32 *) fp;
|
|
pc = rw->ins[7];
|
|
if (!in_sched_functions(pc)) {
|
|
ret = pc;
|
|
goto out;
|
|
}
|
|
fp = rw->ins[6] + bias;
|
|
} while (++count < 16);
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|