switch_to.h 4.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * Copyright (C) 1994, 95, 96, 97, 98, 99, 2003, 06 by Ralf Baechle
 * Copyright (C) 1996 by Paul M. Antoine
 * Copyright (C) 1999 Silicon Graphics
 * Kevin D. Kissell, kevink@mips.org and Carsten Langgaard, carstenl@mips.com
 * Copyright (C) 2000 MIPS Technologies, Inc.
 */
#ifndef _ASM_SWITCH_TO_H
#define _ASM_SWITCH_TO_H

#include <asm/cpu-features.h>
#include <asm/watch.h>
#include <asm/dsp.h>
18
#include <asm/cop2.h>
19
#include <asm/fpu.h>
20 21 22

struct task_struct;

P
Paul Burton 已提交
23 24 25 26 27 28 29 30
/**
 * resume - resume execution of a task
 * @prev:	The task previously executed.
 * @next:	The task to begin executing.
 * @next_ti:	task_thread_info(next).
 *
 * This function is used whilst scheduling to save the context of prev & load
 * the context of next. Returns prev.
31
 */
P
Paul Burton 已提交
32
extern asmlinkage struct task_struct *resume(struct task_struct *prev,
33
		struct task_struct *next, struct thread_info *next_ti);
34 35 36 37 38 39 40

extern unsigned int ll_bit;
extern struct task_struct *ll_task;

#ifdef CONFIG_MIPS_MT_FPAFF

/*
R
Ralf Baechle 已提交
41
 * Handle the scheduler resume end of FPU affinity management.	We do this
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
 * inline to try to keep the overhead down. If we have been forced to run on
 * a "CPU" with an FPU because of a previous high level of FP computation,
 * but did not actually use the FPU during the most recent time-slice (CU1
 * isn't set), we undo the restriction on cpus_allowed.
 *
 * We're not calling set_cpus_allowed() here, because we have no need to
 * force prompt migration - we're already switching the current CPU to a
 * different thread.
 */

#define __mips_mt_fpaff_switch_to(prev)					\
do {									\
	struct thread_info *__prev_ti = task_thread_info(prev);		\
									\
	if (cpu_has_fpu &&						\
	    test_ti_thread_flag(__prev_ti, TIF_FPUBOUND) &&		\
	    (!(KSTK_STATUS(prev) & ST0_CU1))) {				\
		clear_ti_thread_flag(__prev_ti, TIF_FPUBOUND);		\
		prev->cpus_allowed = prev->thread.user_cpus_allowed;	\
	}								\
	next->thread.emulated_fp = 0;					\
} while(0)

#else
#define __mips_mt_fpaff_switch_to(prev) do { (void) (prev); } while (0)
#endif

69 70 71 72 73 74
/*
 * Clear LLBit during context switches on MIPSr6 such that eretnc can be used
 * unconditionally when returning to userland in entry.S.
 */
#define __clear_r6_hw_ll_bit() do {					\
	if (cpu_has_mips_r6)						\
75
		write_c0_lladdr(0);					\
76 77 78 79 80
} while (0)

#define __clear_software_ll_bit() do {					\
	if (!__builtin_constant_p(cpu_has_llsc) || !cpu_has_llsc)	\
		ll_bit = 0;						\
81 82
} while (0)

83 84 85 86
/*
 * Check FCSR for any unmasked exceptions pending set with `ptrace',
 * clear them and send a signal.
 */
87 88
#ifdef CONFIG_MIPS_FP_SUPPORT
# define __sanitize_fcr31(next)						\
89 90 91 92 93 94 95 96 97 98
do {									\
	unsigned long fcr31 = mask_fcr31_x(next->thread.fpu.fcr31);	\
	void __user *pc;						\
									\
	if (unlikely(fcr31)) {						\
		pc = (void __user *)task_pt_regs(next)->cp0_epc;	\
		next->thread.fpu.fcr31 &= ~fcr31;			\
		force_fcr31_sig(fcr31, pc, next);			\
	}								\
} while (0)
99 100 101
#else
# define __sanitize_fcr31(next)
#endif
102

103 104 105 106 107 108
/*
 * For newly created kernel threads switch_to() will return to
 * ret_from_kernel_thread, newly created user threads to ret_from_fork.
 * That is, everything following resume() will be skipped for new threads.
 * So everything that matters to new threads should be placed before resume().
 */
109 110 111
#define switch_to(prev, next, last)					\
do {									\
	__mips_mt_fpaff_switch_to(prev);				\
112
	lose_fpu_inatomic(1, prev);					\
113 114
	if (tsk_used_math(next))					\
		__sanitize_fcr31(next);					\
115
	if (cpu_has_dsp) {						\
116
		__save_dsp(prev);					\
117 118 119 120 121 122 123 124 125 126 127 128 129 130
		__restore_dsp(next);					\
	}								\
	if (cop2_present) {						\
		set_c0_status(ST0_CU2);					\
		if ((KSTK_STATUS(prev) & ST0_CU2)) {			\
			if (cop2_lazy_restore)				\
				KSTK_STATUS(prev) &= ~ST0_CU2;		\
			cop2_save(prev);				\
		}							\
		if (KSTK_STATUS(next) & ST0_CU2 &&			\
		    !cop2_lazy_restore) {				\
			cop2_restore(next);				\
		}							\
		clear_c0_status(ST0_CU2);				\
131
	}								\
132
	__clear_r6_hw_ll_bit();						\
133 134
	__clear_software_ll_bit();					\
	if (cpu_has_userlocal)						\
135
		write_c0_userlocal(task_thread_info(next)->tp_value);	\
J
James Hogan 已提交
136
	__restore_watch(next);						\
137
	(last) = resume(prev, next, task_thread_info(next));		\
138 139 140
} while (0)

#endif /* _ASM_SWITCH_TO_H */