Mateusz Guzik
2016-11-27 21:25:03 UTC
One of the standard problems in mpsafe kernels is false sharing.
The somewhat standard way of combating parts of it for frequently read
and rarely (if ever) modified variables is an annotation which puts
them in a dedicated part of the binary and the somewhat standard name
for a macro doing the work is __read_mostly.
The FreeBSD kernel still does not have it and I think it's long overdue.
Now, I don't know how to do it nicely in the linker script, in
particular I don't know how get the cache line size.
For testing purposes I hacked up a crap version below and verified it
works fine.
I hacked up the following crap version. Would be nice if someone with
ld-clue fixed that up. I don't care about the header either.
I just want the macro. :)
diff --git a/sys/amd64/include/param.h b/sys/amd64/include/param.h
index a619e395..ab66e79 100644
--- a/sys/amd64/include/param.h
+++ b/sys/amd64/include/param.h
@@ -152,4 +152,6 @@
#define INKERNEL(va) (((va) >= DMAP_MIN_ADDRESS && (va) < DMAP_MAX_ADDRESS) \
|| ((va) >= VM_MIN_KERNEL_ADDRESS && (va) < VM_MAX_KERNEL_ADDRESS))
+#define __read_mostly __attribute__((__section__(".data.read_mostly")))
+
#endif /* !_AMD64_INCLUDE_PARAM_H_ */
diff --git a/sys/conf/ldscript.amd64 b/sys/conf/ldscript.amd64
index 5d86b03..ae98447 100644
--- a/sys/conf/ldscript.amd64
+++ b/sys/conf/ldscript.amd64
@@ -151,6 +151,11 @@ SECTIONS
KEEP (*(.gnu.linkonce.d.*personality*))
}
.data1 : { *(.data1) }
+ .data_read_mostly :
+ {
+ *(.data.read_mostly)
+ . = ALIGN(64);
+ }
_edata = .; PROVIDE (edata = .);
__bss_start = .;
.bss :
diff --git a/sys/sys/param.h b/sys/sys/param.h
index cf38985..dcd9526 100644
--- a/sys/sys/param.h
+++ b/sys/sys/param.h
@@ -360,4 +360,8 @@ __END_DECLS
*/
#define __PAST_END(array, offset) (((__typeof__(*(array)) *)(array))[offset])
+#ifndef __read_mostly
+#define __read_mostly
+#endif
+
#endif /* _SYS_PARAM_H_ */
The somewhat standard way of combating parts of it for frequently read
and rarely (if ever) modified variables is an annotation which puts
them in a dedicated part of the binary and the somewhat standard name
for a macro doing the work is __read_mostly.
The FreeBSD kernel still does not have it and I think it's long overdue.
Now, I don't know how to do it nicely in the linker script, in
particular I don't know how get the cache line size.
For testing purposes I hacked up a crap version below and verified it
works fine.
I hacked up the following crap version. Would be nice if someone with
ld-clue fixed that up. I don't care about the header either.
I just want the macro. :)
diff --git a/sys/amd64/include/param.h b/sys/amd64/include/param.h
index a619e395..ab66e79 100644
--- a/sys/amd64/include/param.h
+++ b/sys/amd64/include/param.h
@@ -152,4 +152,6 @@
#define INKERNEL(va) (((va) >= DMAP_MIN_ADDRESS && (va) < DMAP_MAX_ADDRESS) \
|| ((va) >= VM_MIN_KERNEL_ADDRESS && (va) < VM_MAX_KERNEL_ADDRESS))
+#define __read_mostly __attribute__((__section__(".data.read_mostly")))
+
#endif /* !_AMD64_INCLUDE_PARAM_H_ */
diff --git a/sys/conf/ldscript.amd64 b/sys/conf/ldscript.amd64
index 5d86b03..ae98447 100644
--- a/sys/conf/ldscript.amd64
+++ b/sys/conf/ldscript.amd64
@@ -151,6 +151,11 @@ SECTIONS
KEEP (*(.gnu.linkonce.d.*personality*))
}
.data1 : { *(.data1) }
+ .data_read_mostly :
+ {
+ *(.data.read_mostly)
+ . = ALIGN(64);
+ }
_edata = .; PROVIDE (edata = .);
__bss_start = .;
.bss :
diff --git a/sys/sys/param.h b/sys/sys/param.h
index cf38985..dcd9526 100644
--- a/sys/sys/param.h
+++ b/sys/sys/param.h
@@ -360,4 +360,8 @@ __END_DECLS
*/
#define __PAST_END(array, offset) (((__typeof__(*(array)) *)(array))[offset])
+#ifndef __read_mostly
+#define __read_mostly
+#endif
+
#endif /* _SYS_PARAM_H_ */
--
Mateusz Guzik <mjguzik gmail.com>
Mateusz Guzik <mjguzik gmail.com>