Skip to content

Commit 9791554

Browse files
paulburtonralfbaechle
authored andcommitted
MIPS,prctl: add PR_[GS]ET_FP_MODE prctl options for MIPS
Userland code may be built using an ABI which permits linking to objects that have more restrictive floating point requirements. For example, userland code may be built to target the O32 FPXX ABI. Such code may be linked with other FPXX code, or code built for either one of the more restrictive FP32 or FP64. When linking with more restrictive code, the overall requirement of the process becomes that of the more restrictive code. The kernel has no way to know in advance which mode the process will need to be executed in, and indeed it may need to change during execution. The dynamic loader is the only code which will know the overall required mode, and so it needs to have a means to instruct the kernel to switch the FP mode of the process. This patch introduces 2 new options to the prctl syscall which provide such a capability. The FP mode of the process is represented as a simple bitmask combining a number of mode bits mirroring those present in the hardware. Userland can either retrieve the current FP mode of the process: mode = prctl(PR_GET_FP_MODE); or modify the current FP mode of the process: err = prctl(PR_SET_FP_MODE, new_mode); Signed-off-by: Paul Burton <paul.burton@imgtec.com> Cc: Matthew Fortune <matthew.fortune@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/8899/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
1 parent ae58d88 commit 9791554

File tree

7 files changed

+144
-0
lines changed

7 files changed

+144
-0
lines changed

arch/mips/include/asm/mmu.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
#ifndef __ASM_MMU_H
22
#define __ASM_MMU_H
33

4+
#include <linux/atomic.h>
5+
46
typedef struct {
57
unsigned long asid[NR_CPUS];
68
void *vdso;
9+
atomic_t fp_mode_switching;
710
} mm_context_t;
811

912
#endif /* __ASM_MMU_H */

arch/mips/include/asm/mmu_context.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ init_new_context(struct task_struct *tsk, struct mm_struct *mm)
132132
for_each_possible_cpu(i)
133133
cpu_context(i, mm) = 0;
134134

135+
atomic_set(&mm->context.fp_mode_switching, 0);
136+
135137
return 0;
136138
}
137139

arch/mips/include/asm/processor.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,4 +399,15 @@ unsigned long get_wchan(struct task_struct *p);
399399

400400
#endif
401401

402+
/*
403+
* Functions & macros implementing the PR_GET_FP_MODE & PR_SET_FP_MODE options
404+
* to the prctl syscall.
405+
*/
406+
extern int mips_get_process_fp_mode(struct task_struct *task);
407+
extern int mips_set_process_fp_mode(struct task_struct *task,
408+
unsigned int value);
409+
410+
#define GET_FP_MODE(task) mips_get_process_fp_mode(task)
411+
#define SET_FP_MODE(task,value) mips_set_process_fp_mode(task, value)
412+
402413
#endif /* _ASM_PROCESSOR_H */

arch/mips/kernel/process.c

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <linux/completion.h>
2626
#include <linux/kallsyms.h>
2727
#include <linux/random.h>
28+
#include <linux/prctl.h>
2829

2930
#include <asm/asm.h>
3031
#include <asm/bootinfo.h>
@@ -550,3 +551,94 @@ void arch_trigger_all_cpu_backtrace(bool include_self)
550551
{
551552
smp_call_function(arch_dump_stack, NULL, 1);
552553
}
554+
555+
int mips_get_process_fp_mode(struct task_struct *task)
556+
{
557+
int value = 0;
558+
559+
if (!test_tsk_thread_flag(task, TIF_32BIT_FPREGS))
560+
value |= PR_FP_MODE_FR;
561+
if (test_tsk_thread_flag(task, TIF_HYBRID_FPREGS))
562+
value |= PR_FP_MODE_FRE;
563+
564+
return value;
565+
}
566+
567+
int mips_set_process_fp_mode(struct task_struct *task, unsigned int value)
568+
{
569+
const unsigned int known_bits = PR_FP_MODE_FR | PR_FP_MODE_FRE;
570+
unsigned long switch_count;
571+
struct task_struct *t;
572+
573+
/* Check the value is valid */
574+
if (value & ~known_bits)
575+
return -EOPNOTSUPP;
576+
577+
/* Avoid inadvertently triggering emulation */
578+
if ((value & PR_FP_MODE_FR) && cpu_has_fpu &&
579+
!(current_cpu_data.fpu_id & MIPS_FPIR_F64))
580+
return -EOPNOTSUPP;
581+
if ((value & PR_FP_MODE_FRE) && cpu_has_fpu && !cpu_has_fre)
582+
return -EOPNOTSUPP;
583+
584+
/* Save FP & vector context, then disable FPU & MSA */
585+
if (task->signal == current->signal)
586+
lose_fpu(1);
587+
588+
/* Prevent any threads from obtaining live FP context */
589+
atomic_set(&task->mm->context.fp_mode_switching, 1);
590+
smp_mb__after_atomic();
591+
592+
/*
593+
* If there are multiple online CPUs then wait until all threads whose
594+
* FP mode is about to change have been context switched. This approach
595+
* allows us to only worry about whether an FP mode switch is in
596+
* progress when FP is first used in a tasks time slice. Pretty much all
597+
* of the mode switch overhead can thus be confined to cases where mode
598+
* switches are actually occuring. That is, to here. However for the
599+
* thread performing the mode switch it may take a while...
600+
*/
601+
if (num_online_cpus() > 1) {
602+
spin_lock_irq(&task->sighand->siglock);
603+
604+
for_each_thread(task, t) {
605+
if (t == current)
606+
continue;
607+
608+
switch_count = t->nvcsw + t->nivcsw;
609+
610+
do {
611+
spin_unlock_irq(&task->sighand->siglock);
612+
cond_resched();
613+
spin_lock_irq(&task->sighand->siglock);
614+
} while ((t->nvcsw + t->nivcsw) == switch_count);
615+
}
616+
617+
spin_unlock_irq(&task->sighand->siglock);
618+
}
619+
620+
/*
621+
* There are now no threads of the process with live FP context, so it
622+
* is safe to proceed with the FP mode switch.
623+
*/
624+
for_each_thread(task, t) {
625+
/* Update desired FP register width */
626+
if (value & PR_FP_MODE_FR) {
627+
clear_tsk_thread_flag(t, TIF_32BIT_FPREGS);
628+
} else {
629+
set_tsk_thread_flag(t, TIF_32BIT_FPREGS);
630+
clear_tsk_thread_flag(t, TIF_MSA_CTX_LIVE);
631+
}
632+
633+
/* Update desired FP single layout */
634+
if (value & PR_FP_MODE_FRE)
635+
set_tsk_thread_flag(t, TIF_HYBRID_FPREGS);
636+
else
637+
clear_tsk_thread_flag(t, TIF_HYBRID_FPREGS);
638+
}
639+
640+
/* Allow threads to use FP again */
641+
atomic_set(&task->mm->context.fp_mode_switching, 0);
642+
643+
return 0;
644+
}

arch/mips/kernel/traps.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1134,10 +1134,29 @@ static int default_cu2_call(struct notifier_block *nfb, unsigned long action,
11341134
return NOTIFY_OK;
11351135
}
11361136

1137+
static int wait_on_fp_mode_switch(atomic_t *p)
1138+
{
1139+
/*
1140+
* The FP mode for this task is currently being switched. That may
1141+
* involve modifications to the format of this tasks FP context which
1142+
* make it unsafe to proceed with execution for the moment. Instead,
1143+
* schedule some other task.
1144+
*/
1145+
schedule();
1146+
return 0;
1147+
}
1148+
11371149
static int enable_restore_fp_context(int msa)
11381150
{
11391151
int err, was_fpu_owner, prior_msa;
11401152

1153+
/*
1154+
* If an FP mode switch is currently underway, wait for it to
1155+
* complete before proceeding.
1156+
*/
1157+
wait_on_atomic_t(&current->mm->context.fp_mode_switching,
1158+
wait_on_fp_mode_switch, TASK_KILLABLE);
1159+
11411160
if (!used_math()) {
11421161
/* First time FP context user. */
11431162
preempt_disable();

include/uapi/linux/prctl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,4 +185,9 @@ struct prctl_mm_map {
185185
#define PR_MPX_ENABLE_MANAGEMENT 43
186186
#define PR_MPX_DISABLE_MANAGEMENT 44
187187

188+
#define PR_SET_FP_MODE 45
189+
#define PR_GET_FP_MODE 46
190+
# define PR_FP_MODE_FR (1 << 0) /* 64b FP registers */
191+
# define PR_FP_MODE_FRE (1 << 1) /* 32b compatibility */
192+
188193
#endif /* _LINUX_PRCTL_H */

kernel/sys.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,12 @@
9797
#ifndef MPX_DISABLE_MANAGEMENT
9898
# define MPX_DISABLE_MANAGEMENT(a) (-EINVAL)
9999
#endif
100+
#ifndef GET_FP_MODE
101+
# define GET_FP_MODE(a) (-EINVAL)
102+
#endif
103+
#ifndef SET_FP_MODE
104+
# define SET_FP_MODE(a,b) (-EINVAL)
105+
#endif
100106

101107
/*
102108
* this is where the system-wide overflow UID and GID are defined, for
@@ -2215,6 +2221,12 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
22152221
case PR_MPX_DISABLE_MANAGEMENT:
22162222
error = MPX_DISABLE_MANAGEMENT(me);
22172223
break;
2224+
case PR_SET_FP_MODE:
2225+
error = SET_FP_MODE(me, arg2);
2226+
break;
2227+
case PR_GET_FP_MODE:
2228+
error = GET_FP_MODE(me);
2229+
break;
22182230
default:
22192231
error = -EINVAL;
22202232
break;

0 commit comments

Comments
 (0)