Skip to contentSkip to conversation

on kernel_eflags

Q x86-64 In review Cyrill Gorcunov 7 participants 19 messages Jul 5, 2011 – Jul 7, 2011 x86

lore
Share & tools
lore
#0 lore raw

While were looking into ret_from_fork code I somehow wondered about the global kernel_eflags variable here.

arch/x86/cpu/common.c
---------------------
unsigned long kernel_eflags;

void __cpuinit cpu_init(void)

{
	...
	raw_local_save_flags(kernel_eflags);
}

arch/x86/kernel/entry_64.S
--------------------------
ENTRY(ret_from_fork)

	DEFAULT_FRAME
	LOCK ; btr $TIF_FORK,TI_flags(%r8)
	pushq_cfi kernel_eflags(%rip)
	popfq_cfi                               # reset kernel eflags

Every call to cpu_init renew global kernel_eflags and every task switching does use this variable in a sake of cleaning carry bit of flags register as far as I can tell.

Should not every cpu has own copy of kernel_eflags? Just to be consistent in style? Or this would be space waisting and an optimization is done here?

Cyrill

#1 lore raw
Quoted 21 lines · Cyrill Gorcunov

While were looking into ret_from_fork code I somehow wondered about the global kernel_eflags variable here.

arch/x86/cpu/common.c
---------------------
unsigned long kernel_eflags;

void __cpuinit cpu_init(void)

{
	...
	raw_local_save_flags(kernel_eflags);
}

arch/x86/kernel/entry_64.S
--------------------------
ENTRY(ret_from_fork)

	DEFAULT_FRAME
	LOCK ; btr $TIF_FORK,TI_flags(%r8)
	pushq_cfi kernel_eflags(%rip)
	popfq_cfi                               # reset kernel eflags
Quoted 8 lines

Every call to cpu_init renew global kernel_eflags and every task switching does use this variable in a sake of cleaning carry bit of flags register as far as I can tell.

Should not every cpu has own copy of kernel_eflags? Just to be consistent in style? Or this would be space waisting and an optimization is done here?

I noticed this a while ago and couldn't figure out why it was done this way. On 32-bit the initial EFLAGS is simply hardcoded and that seems to make sense to me since it's not clear that the specific value of EFLAGS at the point it is saved in cpu_init() has any particular meaning.

I posted http://lkml.org/lkml/2010/2/9/155 to switch to the hardcoded version for 64 bit too. Is it worth my rebasing and reposting that?

(http://lkml.org/lkml/2010/2/9/152 and http://lkml.org/lkml/2010/2/9/154
were followon cleanups).

Ian.

Signature · 5 lines
Ian Campbell
Current Noise: Kylesa - Drop Out

"... all the modern inconveniences ..."
		-- Mark Twain
#2 lore raw

On Tue, Jul 05, 2011 at 12:00:56PM +0100, Ian Campbell wrote:
...

Quoted 3 lines

I posted http://lkml.org/lkml/2010/2/9/155 to switch to the hardcoded version for 64 bit too. Is it worth my rebasing and reposting that?

I think so.

Cyrill

#3 lore raw

PATCH 1/3 x86: drop unnecessary kernel_eflags variable from 64 bit.

For no reason that I can determine 64 bit x86 saves the current eflags in cpu_init purely for use in ret_from_fork. The equivalent 32 bit code simply hard codes 0x0202 as the new EFLAGS which seems safer than relying on a potentially arbitrary EFLAGS saved during cpu_init.

Original i386 changeset
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=47a5c6fa0e204a2b63309c648bb2fde36836c826
Original x86_64 changset
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=658fdbef66e5e9be79b457edc2cbbb3add840aa9

The only comment in the later indicates that it is following the former, but not why it differs in this way.

This change makes 64 bit use the same mechanism to setup the initial EFLAGS on fork. Note that 64 bit resets EFLAGS before calling schedule_tail() as opposed to 32 bit which calls schedule_tail() first. Therefore the correct value for EFLAGS has opposite IF bit. This will be fixed in a subsequent patch.

---
 arch/x86/kernel/cpu/common.c |    4 ----
 arch/x86/kernel/entry_64.S   |    2 +-
 2 files changed, 1 insertions(+), 5 deletions(-)
arch/x86/kernel/cpu/common.c +0 -4
index 22a073d..178c3b4 100644
@@ -1077,8 +1077,6 @@ void syscall_init(void)
 	       X86_EFLAGS_TF|X86_EFLAGS_DF|X86_EFLAGS_IF|X86_EFLAGS_IOPL);
 }
 
-unsigned long kernel_eflags;
-
 /*
  * Copies of the original ist values from the tss are only accessed during
  * debugging, no special alignment required.
@@ -1231,8 +1229,6 @@ void __cpuinit cpu_init(void)
 	fpu_init();
 	xsave_init();
 
-	raw_local_save_flags(kernel_eflags);
-
 	if (is_uv_system())
 		uv_cpu_init();
 }
arch/x86/kernel/entry_64.S +1 -1
index e13329d..b50d142 100644
@@ -396,7 +396,7 @@ ENTRY(ret_from_fork)
 
 	LOCK ; btr $TIF_FORK,TI_flags(%r8)
 
-	pushq_cfi kernel_eflags(%rip)
+	pushq_cfi $0x0002
 	popfq_cfi				# reset kernel eflags
 
 	call schedule_tail			# rdi: 'prev' task parameter
Signature · 1 line
1.7.2.5
#4 lore raw

Re: [PATCH 1/3] x86: drop unnecessary kernel_eflags variable from 64 bit.

Quoted 13 lines · Ian Campbell

For no reason that I can determine 64 bit x86 saves the current eflags in cpu_init purely for use in ret_from_fork. The equivalent 32 bit code simply hard codes 0x0202 as the new EFLAGS which seems safer than relying on a potentially arbitrary EFLAGS saved during cpu_init.

Original i386 changeset
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=47a5c6fa0e204a2b63309c648bb2fde36836c826
Original x86_64 changset
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=658fdbef66e5e9be79b457edc2cbbb3add840aa9

The only comment in the later indicates that it is following the former, but not why it differs in this way.

Quoted 8 lines

This change makes 64 bit use the same mechanism to setup the initial EFLAGS on fork. Note that 64 bit resets EFLAGS before calling schedule_tail() as opposed to 32 bit which calls schedule_tail() first. Therefore the correct value for EFLAGS has opposite IF bit. This will be fixed in a subsequent patch.

The whole series looks good to me, thanks Ian! I hope I don't miss anything, so lets wait for more feedback ;)

Cyrill

#5 lore raw

Re: [PATCH 1/3] x86: drop unnecessary kernel_eflags variable from 64 bit.

Quoted 20 lines · Cyrill Gorcunov
Quoted 19 lines · Ian Campbell

For no reason that I can determine 64 bit x86 saves the current eflags in cpu_init purely for use in ret_from_fork. The equivalent 32 bit code simply hard codes 0x0202 as the new EFLAGS which seems safer than relying on a potentially arbitrary EFLAGS saved during cpu_init.

Original i386 changeset
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=47a5c6fa0e204a2b63309c648bb2fde36836c826
Original x86_64 changset
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=658fdbef66e5e9be79b457edc2cbbb3add840aa9

The only comment in the later indicates that it is following the former, but not why it differs in this way.

This change makes 64 bit use the same mechanism to setup the initial EFLAGS on fork. Note that 64 bit resets EFLAGS before calling schedule_tail() as opposed to 32 bit which calls schedule_tail() first. Therefore the correct value for EFLAGS has opposite IF bit. This will be fixed in a subsequent patch.

Quoted 5 lines
Quoted 2 lines

The whole series looks good to me, thanks Ian! I hope I don't miss anything, so lets wait for more feedback ;)

Thanks. Actually I missed the extern declaration in asm/processor.h so this updated patch should be used instead:

8<-----------------------------------------------

Quoted 1 line

From a450bf9e1c6e3f436c652b3e4d63b50c1f9848d4 Mon Sep 17 00:00:00 2001

From: Ian Campbell <[email protected]>
Date: Wed, 6 Jul 2011 10:24:46 +0100
Subject: [PATCH] x86: drop unnecessary kernel_eflags variable from 64 bit.

For no reason that I can determine 64 bit x86 saves the current eflags in cpu_init purely for use in ret_from_fork. The equivalent 32 bit code simply hard codes 0x0202 as the new EFLAGS which seems safer than relying on a potentially arbitrary EFLAGS saved during cpu_init.

Original i386 changeset
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=47a5c6fa0e204a2b63309c648bb2fde36836c826
Original x86_64 changset
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=658fdbef66e5e9be79b457edc2cbbb3add840aa9

The only comment in the later indicates that it is following the former, but not why it differs in this way.

This change makes 64 bit use the same mechanism to setup the initial EFLAGS on fork. Note that 64 bit resets EFLAGS before calling schedule_tail() as opposed to 32 bit which calls schedule_tail() first. Therefore the correct value for EFLAGS has opposite IF bit. This will be fixed in a subsequent patch.

---
 arch/x86/include/asm/processor.h |    1 -
 arch/x86/kernel/cpu/common.c     |    4 ----
 arch/x86/kernel/entry_64.S       |    2 +-
 3 files changed, 1 insertions(+), 6 deletions(-)
arch/x86/include/asm/processor.h +0 -1
index 2193715..698af88 100644
@@ -398,7 +398,6 @@ DECLARE_INIT_PER_CPU(irq_stack_union);
 
 DECLARE_PER_CPU(char *, irq_stack_ptr);
 DECLARE_PER_CPU(unsigned int, irq_count);
-extern unsigned long kernel_eflags;
 extern asmlinkage void ignore_sysret(void);
 #else	/* X86_64 */
 #ifdef CONFIG_CC_STACKPROTECTOR
arch/x86/kernel/cpu/common.c +0 -4
index 22a073d..178c3b4 100644
@@ -1077,8 +1077,6 @@ void syscall_init(void)
 	       X86_EFLAGS_TF|X86_EFLAGS_DF|X86_EFLAGS_IF|X86_EFLAGS_IOPL);
 }
 
-unsigned long kernel_eflags;
-
 /*
  * Copies of the original ist values from the tss are only accessed during
  * debugging, no special alignment required.
@@ -1231,8 +1229,6 @@ void __cpuinit cpu_init(void)
 	fpu_init();
 	xsave_init();
 
-	raw_local_save_flags(kernel_eflags);
-
 	if (is_uv_system())
 		uv_cpu_init();
 }
arch/x86/kernel/entry_64.S +1 -1
index e13329d..b50d142 100644
@@ -396,7 +396,7 @@ ENTRY(ret_from_fork)
 
 	LOCK ; btr $TIF_FORK,TI_flags(%r8)
 
-	pushq_cfi kernel_eflags(%rip)
+	pushq_cfi $0x0002
 	popfq_cfi				# reset kernel eflags
 
 	call schedule_tail			# rdi: 'prev' task parameter
Signature · 1 line
1.7.2.5
#6 lore raw

Re: [PATCH 1/3] x86: drop unnecessary kernel_eflags variable from 64 bit.

On Wed, Jul 06, 2011 at 10:25:29AM +0100, Ian Campbell wrote:
...

Quoted 7 lines
Quoted 3 lines

The whole series looks good to me, thanks Ian! I hope I don't miss anything, so lets wait for more feedback ;)

Thanks. Actually I missed the extern declaration in asm/processor.h so this updated patch should be used instead:

Great, thanks Ian. Have you a chance to run this series? ;)

Cyrill

#7 lore raw

Re: [PATCH 1/3] x86: drop unnecessary kernel_eflags variable from 64 bit.

Quoted 11 lines · Cyrill Gorcunov

On Wed, Jul 06, 2011 at 10:25:29AM +0100, Ian Campbell wrote:
...

Quoted 7 lines
Quoted 3 lines

The whole series looks good to me, thanks Ian! I hope I don't miss anything, so lets wait for more feedback ;)

Thanks. Actually I missed the extern declaration in asm/processor.h so this updated patch should be used instead:

Great, thanks Ian. Have you a chance to run this series? ;)

Yeah I booted it before I posted it. I figured that the initscripts etc would be a sufficient test of fork() and that if it were wrong it would fall over pretty quickly and obviously.

Ian.

#8 lore raw

Re: [PATCH 1/3] x86: drop unnecessary kernel_eflags variable from 64 bit.

Quoted 11 lines · Ian Campbell
Quoted 10 lines · Cyrill Gorcunov

On Wed, Jul 06, 2011 at 10:25:29AM +0100, Ian Campbell wrote:
...

Quoted 7 lines
Quoted 3 lines

The whole series looks good to me, thanks Ian! I hope I don't miss anything, so lets wait for more feedback ;)

Thanks. Actually I missed the extern declaration in asm/processor.h so this updated patch should be used instead:

Quoted 8 lines
Quoted 1 line

Great, thanks Ian. Have you a chance to run this series? ;)

Yeah I booted it before I posted it. I figured that the initscripts etc would be a sufficient test of fork() and that if it were wrong it would fall over pretty quickly and obviously.

Ian.

OK, thanks!

Cyrill

#9 lore raw

Re: [PATCH 1/3] x86: drop unnecessary kernel_eflags variable from 64 bit.

In reply to Ian Campbell
Quoted 5 lines · Ian Campbell

For no reason that I can determine 64 bit x86 saves the current eflags in cpu_init purely for use in ret_from_fork. The equivalent 32 bit code simply hard codes 0x0202 as the new EFLAGS which seems safer than relying on a potentially arbitrary EFLAGS saved during cpu_init.

Quoted 7 lines

Original i386 changeset
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=47a5c6fa0e204a2b63309c648bb2fde36836c826
Original x86_64 changset
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=658fdbef66e5e9be79b457edc2cbbb3add840aa9

The only comment in the later indicates that it is following the former, but not why it differs in this way.

Well lets ask Linus and Andi?

Quoted 48 lines

This change makes 64 bit use the same mechanism to setup the initial EFLAGS on fork. Note that 64 bit resets EFLAGS before calling schedule_tail() as opposed to 32 bit which calls schedule_tail() first. Therefore the correct value for EFLAGS has opposite IF bit. This will be fixed in a subsequent patch.

---
 arch/x86/kernel/cpu/common.c |    4 ----
 arch/x86/kernel/entry_64.S   |    2 +-

 2 files changed, 1 insertions(+), 5 deletions(-)
arch/x86/kernel/cpu/common.c +0 -4
index 22a073d..178c3b4 100644
@@ -1077,8 +1077,6 @@ void syscall_init(void)
               X86_EFLAGS_TF|X86_EFLAGS_DF|X86_EFLAGS_IF|X86_EFLAGS_IOPL);
  }
 
-unsigned long kernel_eflags;
-
  /*
  * Copies of the original ist values from the tss are only accessed during
  * debugging, no special alignment required.
@@ -1231,8 +1229,6 @@ void __cpuinit cpu_init(void)
        fpu_init();
        xsave_init();
 
-       raw_local_save_flags(kernel_eflags);
-
        if (is_uv_system())
                uv_cpu_init();
  }
arch/x86/kernel/entry_64.S +1 -1
index e13329d..b50d142 100644
@@ -396,7 +396,7 @@ ENTRY(ret_from_fork)
 
        LOCK ; btr $TIF_FORK,TI_flags(%r8)
 
-       pushq_cfi kernel_eflags(%rip)
+       pushq_cfi $0x0002

       popfq_cfi                               # reset kernel eflags

       call schedule_tail                      # rdi: 'prev' task parameter

#10 lore raw

Re: [PATCH 1/3] x86: drop unnecessary kernel_eflags variable from 64 bit.

Quoted 1 line

Well lets ask Linus and Andi?

The original idea was that if someone extends EFLAGS with new bits the BIOS could set them. Strictly said the Intel Manual specifies that ("reserved bits -- always set to values previously read")

But that never really worked out and I don't think anyone would dare extending it now. So hardcoding is fine I guess.

-Andi

#11 lore raw

Re: [PATCH 1/3] x86: drop unnecessary kernel_eflags variable from 64 bit.

Quoted 9 lines · Andi Kleen
Quoted 1 line

Well lets ask Linus and Andi?

The original idea was that if someone extends EFLAGS with new bits the BIOS could set them. Strictly said the Intel Manual specifies that ("reserved bits -- always set to values previously read")

But that never really worked out and I don't think anyone would dare extending it now. So hardcoding is fine I guess.

We already clear any undefined flags almost as soon as we enter the kernel... and quite rightly so, because there are BIOSes which leave random crap in the flags.

It is very different from what an *application* should do in this case, however.

-hpa

#12 lore raw

PATCH 2/3 x86: make 64 bit ret_from_fork a little more similar to 32 bit

In reply to Cyrill Gorcunov

The 64 bit version resets EFLAGS before calling schedule_tail() and
therefore leaves EFLAGS.IF clear. 32 bit resets EFLAGS after calling schedule_tail() and therefore leaves EFLAGS.IF set. I don't think
there is any practical difference between the two approaches since
interrupts are actually reenabled within schedule_tail (schedule_tail->finish_task_switch->finish_lock_switch->raw_spin_unlock_irq->...->local_irq_enable) so arbitrarily pick the 32 bit version and make 64 bit look like that.

---
 arch/x86/kernel/entry_64.S |    7 ++++---
 1 files changed, 4 insertions(+), 3 deletions(-)
arch/x86/kernel/entry_64.S +4 -3
index b50d142..9c28bd5 100644
@@ -394,13 +394,14 @@ END(save_paranoid)
 ENTRY(ret_from_fork)
 	DEFAULT_FRAME
 
-	LOCK ; btr $TIF_FORK,TI_flags(%r8)
 
-	pushq_cfi $0x0002
-	popfq_cfi				# reset kernel eflags
+	LOCK ; btr $TIF_FORK,TI_flags(%r8)
 
 	call schedule_tail			# rdi: 'prev' task parameter
 
+	pushq_cfi $0x0202
+	popfq_cfi				# reset kernel eflags
+
 	GET_THREAD_INFO(%rcx)
 
 	RESTORE_REST
Signature · 1 line
1.7.2.5
Reviewed-by on v1 2/3
#13 lore raw

Re: [PATCH 2/3] x86: make 64 bit ret_from_fork a little more similar to 32 bit

Quoted 10 lines · Ian Campbell

The 64 bit version resets EFLAGS before calling schedule_tail() and
therefore leaves EFLAGS.IF clear. 32 bit resets EFLAGS after calling schedule_tail() and therefore leaves EFLAGS.IF set. I don't think
there is any practical difference between the two approaches since
interrupts are actually reenabled within schedule_tail (schedule_tail->finish_task_switch->finish_lock_switch->raw_spin_unlock_irq->...->local_irq_enable) so arbitrarily pick the 32 bit version and make 64 bit look like that.

#14 lore raw

PATCH 3/3 x86: ret_from_fork: use symbolic contants for bits in EFLAGS

In reply to Cyrill Gorcunov
---
 arch/x86/kernel/entry_32.S |    2 +-
 arch/x86/kernel/entry_64.S |    2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
arch/x86/kernel/entry_32.S +1 -1
index 5c1a9197..f032530 100644
@@ -303,7 +303,7 @@ ENTRY(ret_from_fork)
 	call schedule_tail
 	GET_THREAD_INFO(%ebp)
 	popl_cfi %eax
-	pushl_cfi $0x0202		# Reset kernel eflags
+	pushl_cfi $(X86_EFLAGS_IF|0x2)	# Reset kernel eflags
 	popfl_cfi
 	jmp syscall_exit
 	CFI_ENDPROC
arch/x86/kernel/entry_64.S +1 -1
index 9c28bd5..4c76abf 100644
@@ -399,7 +399,7 @@ ENTRY(ret_from_fork)
 
 	call schedule_tail			# rdi: 'prev' task parameter
 
-	pushq_cfi $0x0202
+	pushq_cfi $(X86_EFLAGS_IF|0x2)
 	popfq_cfi				# reset kernel eflags
 
 	GET_THREAD_INFO(%rcx)
Signature · 1 line
1.7.2.5
Reviewed-by on v1 3/3
#15 lore raw

Re: [PATCH 3/3] x86: ret_from_fork: use symbolic contants for bits in EFLAGS

Quoted 28 lines · Ian Campbell

---
 arch/x86/kernel/entry_32.S |    2 +-
 arch/x86/kernel/entry_64.S |    2 +-

 2 files changed, 2 insertions(+), 2 deletions(-)
arch/x86/kernel/entry_32.S +0 -0
index 5c1a9197..f032530 100644
@@ -303,7 +303,7 @@ ENTRY(ret_from_fork)

       call schedule_tail
       GET_THREAD_INFO(%ebp)
       popl_cfi %eax
-       pushl_cfi $0x0202               # Reset kernel eflags
+       pushl_cfi $(X86_EFLAGS_IF|0x2)  # Reset kernel eflags popfl_cfi
       jmp syscall_exit
       CFI_ENDPROC

arch/x86/kernel/entry_64.S +0 -0
index 9c28bd5..4c76abf 100644
@@ -399,7 +399,7 @@ ENTRY(ret_from_fork)
 

       call schedule_tail                      # rdi: 'prev' task parameter

Quoted 5 lines

-       pushq_cfi $0x0202
+       pushq_cfi $(X86_EFLAGS_IF|0x2)
       popfq_cfi                               # reset kernel eflags

       GET_THREAD_INFO(%rcx)

#16 lore raw
In reply to Cyrill Gorcunov
Quoted 5 lines · Cyrill Gorcunov

Should not every cpu has own copy of kernel_eflags? Just to be consistent in style? Or this would be space waisting and an optimization is done here?

Not specific to this particular case, but in general: a shared variable that used often but rarely written to will automatically replicate itself in the caches of multiple processors. This is the purpose of the read_mostly segment (writes are permitted but expected to be rare), which exists to make sure that a frequently written variable doesn't randomly end up in the cache line next to a read-mostly variable.

-hpa

Signature · 2 lines
H. Peter Anvin, Intel Open Source Technology Center
I work for Intel.  I don't speak on their behalf.
#17 lore raw
Quoted 7 lines · H. Peter Anvin
Quoted 5 lines · Cyrill Gorcunov

Should not every cpu has own copy of kernel_eflags? Just to be consistent in style? Or this would be space waisting and an optimization is done here?

Quoted 9 lines

Not specific to this particular case, but in general: a shared variable that used often but rarely written to will automatically replicate itself in the caches of multiple processors. This is the purpose of the read_mostly segment (writes are permitted but expected to be rare), which exists to make sure that a frequently written variable doesn't randomly end up in the cache line next to a read-mostly variable.

-hpa

yeah, thanks peter!

Cyrill

#18 lore raw
In reply to H. Peter Anvin
Quoted 7 lines · H. Peter Anvin
Quoted 5 lines · Cyrill Gorcunov

Should not every cpu has own copy of kernel_eflags? Just to be consistent in style? Or this would be space waisting and an optimization is done here?

Quoted 6 lines

Not specific to this particular case, but in general: a shared variable that used often but rarely written to will automatically replicate itself in the caches of multiple processors. This is the purpose of the read_mostly segment (writes are permitted but expected to be rare), which exists to make sure that a frequently written variable doesn't randomly end up in the cache line next to a read-mostly variable.

FWIW the variable in this particular case isn't actually marked as __read_mostly...

Ian.

Signature · 5 lines
Ian Campbell

* Simunye is on a oc3->oc12
 simmy: bite me. :)
 daemon: okay :)