Skip to contentSkip to conversation

jump-label/x86: Compress jmps to 2 bytes where possible

RFC PATCH In review Steven Rostedt 4 participants 12 messages Jan 18, 2012 – Jan 19, 2012

Subsystems (3)
lore
Share & tools
lore

Steven Rostedt

author reviewer
#0 lore raw

cover jump-label/x86: Compress jmps to 2 bytes where possible

As discussed previously, it can save a lot of text space if we could use the 2 byte jump for jump labels instead of always using the 5 byte version. But to do this, the jmps must be put into the kernel first (always on). Then at compile time, the jmps are examined and converted to nops.

At boot up, the jump-labels are examined again and the nops are converted to the CPU native best 5 byte nops for just the 5 byte nop locations.

The actual op is examined to determine if the jmp was 2 or 5 bytes, and dealt with accordingly.

I forward ported old work I did before and cleaned it up a bit. This can also be made to make a "default on" case at compile time if necessary.

Thoughts?

-- Steve

Steven Rostedt (2):
jump labels: Add infrastructure to update jump labels at compile time jump labels/x86: Use etiher 5 byte or 2 byte jumps

----

 Makefile                          |    7 +
 arch/Kconfig                      |    6 +
 arch/x86/Kconfig                  |    1 +
 arch/x86/include/asm/jump_label.h |    2 +-
 arch/x86/kernel/jump_label.c      |   86 +++++++++-
 scripts/Makefile                  |    1 +
 scripts/Makefile.build            |   15 ++-
 scripts/update_jump_label.c       |  335 +++++++++++++++++++++++++++++++++++++
 scripts/update_jump_label.h       |  283 +++++++++++++++++++++++++++++++
 9 files changed, 726 insertions(+), 10 deletions(-)
author reviewer
#1 lore raw

PATCH 1/2 jump labels: Add infrastructure to update jump labels at compile time

From: Steven Rostedt <[email protected]>

Add the infrastructure to allow architectures to modify the jump label locations at compile time. This is mainly for x86, where the jmps may be either 2 bytes or 5 bytes. Instead of wasting 5 bytes for all jump labels, this code will let x86 put in a jmp instead of a 5 byte nop. Then the assembler will make either a 2 byte or 5 byte jmp depending on where the target is.

At compile time, this code will replace the jmps with either a 2 byte or 5 byte nop depending on the size of the jmp that was added.

---
 Makefile                    |    7 +
 arch/Kconfig                |    6 +
 scripts/Makefile            |    1 +
 scripts/Makefile.build      |   15 ++-
 scripts/update_jump_label.c |  335 +++++++++++++++++++++++++++++++++++++++++++
 scripts/update_jump_label.h |  283 ++++++++++++++++++++++++++++++++++++
 6 files changed, 645 insertions(+), 2 deletions(-)
 create mode 100644 scripts/update_jump_label.c
 create mode 100644 scripts/update_jump_label.h
Makefile +7 -0
index adddd11..2b3fdf3 100644
@@ -611,6 +611,13 @@ ifdef CONFIG_DYNAMIC_FTRACE
 endif
 endif
 
+ifdef CONFIG_JUMP_LABEL
+	ifdef CONFIG_HAVE_BUILD_TIME_JUMP_LABEL
+		BUILD_UPDATE_JUMP_LABEL := y
+		export BUILD_UPDATE_JUMP_LABEL
+	endif
+endif
+
 # We trigger additional mismatches with less inlining
 ifdef CONFIG_DEBUG_SECTION_MISMATCH
 KBUILD_CFLAGS += $(call cc-option, -fno-inline-functions-called-once)
arch/Kconfig +6 -0
index 4b0669c..8fa6934 100644
@@ -169,6 +169,12 @@ config HAVE_PERF_EVENTS_NMI
 	  subsystem.  Also has support for calculating CPU cycle events
 	  to determine how many clock cycles in a given period.
 
+config HAVE_BUILD_TIME_JUMP_LABEL
+       bool
+       help
+	If an arch uses scripts/update_jump_label to patch in jump nops
+	at build time, then it must enable this option.
+
 config HAVE_ARCH_JUMP_LABEL
 	bool
 
scripts/Makefile +1 -0
index df7678f..738b65c 100644
@@ -13,6 +13,7 @@ hostprogs-$(CONFIG_LOGO)         += pnmtologo
 hostprogs-$(CONFIG_VT)           += conmakehash
 hostprogs-$(CONFIG_IKCONFIG)     += bin2c
 hostprogs-$(BUILD_C_RECORDMCOUNT) += recordmcount
+hostprogs-$(BUILD_UPDATE_JUMP_LABEL) += update_jump_label
 
 always		:= $(hostprogs-y) $(hostprogs-m)
 
scripts/Makefile.build +13 -2
index d2b366c..8a84b80 100644
@@ -258,6 +258,15 @@ cmd_modversions =								\
 	fi;
 endif
 
+ifdef BUILD_UPDATE_JUMP_LABEL
+update_jump_label_source := $(srctree)/scripts/update_jump_label.c \
+			$(srctree)/scripts/update_jump_label.h
+cmd_update_jump_label =						\
+	if [ $(@) != "scripts/mod/empty.o" ]; then		\
+		$(objtree)/scripts/update_jump_label "$(@)";	\
+	fi;
+endif
+
 ifdef CONFIG_FTRACE_MCOUNT_RECORD
 ifdef BUILD_C_RECORDMCOUNT
 ifeq ("$(origin RECORDMCOUNT_WARN)", "command line")
@@ -294,6 +303,7 @@ define rule_cc_o_c
 	$(cmd_modversions)						  \
 	$(call echo-cmd,record_mcount)					  \
 	$(cmd_record_mcount)						  \
+	$(cmd_update_jump_label)					  \
 	scripts/basic/fixdep $(depfile) $@ '$(call make-cmd,cc_o_c)' >    \
 	                                              $(dot-target).tmp;  \
 	rm -f $(depfile);						  \
@@ -301,13 +311,14 @@ define rule_cc_o_c
 endef
 
 # Built-in and composite module parts
-$(obj)/%.o: $(src)/%.c $(recordmcount_source) FORCE
+$(obj)/%.o: $(src)/%.c $(recordmcount_source) $(update_jump_label_source) FORCE
 	$(call cmd,force_checksrc)
 	$(call if_changed_rule,cc_o_c)
 
 # Single-part modules are special since we need to mark them in $(MODVERDIR)
 
-$(single-used-m): $(obj)/%.o: $(src)/%.c $(recordmcount_source) FORCE
+$(single-used-m): $(obj)/%.o: $(src)/%.c $(recordmcount_source) \
+		  $(update_jump_label_source) FORCE
 	$(call cmd,force_checksrc)
 	$(call if_changed_rule,cc_o_c)
 	@{ echo $(@:.o=.ko); echo $@; } > $(MODVERDIR)/$(@F:.o=.mod)
scripts/update_jump_label.c new file +335 -0

335 lines not on this page · raw message

scripts/update_jump_label.h new file +283 -0

283 lines not on this page · raw message

Signature · 1 line
1.7.8.3
#2 lore raw

Re: [PATCH 1/2] jump labels: Add infrastructure to update jump labels at compile time

Hi Steven,

I really like this patchset! A few comments below,

Quoted 4 lines · * Steven Rostedt ([email protected]) wrote:

From: Steven Rostedt <[email protected]>

Add the infrastructure to allow architectures to modify the jump label locations at compile time. This is mainly for x86, where the jmps may

is it "at compile time" or "after compilation" ? AFAIU, this update_jump_label script gets executed on the .o, which makes it technically more part of the linking stage than the compilation stage.

Quoted 8 lines

be either 2 bytes or 5 bytes. Instead of wasting 5 bytes for all jump labels, this code will let x86 put in a jmp instead of a 5 byte nop. Then the assembler will make either a 2 byte or 5 byte jmp depending on where the target is.

At compile time, this code will replace the jmps with either a 2 byte or 5 byte nop depending on the size of the jmp that was added.

A small note somewhere saying that this 2 vs 5 bytes choice done by the compiler is a pessimistic approximation would be good, so people don't end up complaining if they see that the compiler chose a 5-byte nop for a 120 bytes offset.

Quoted 12 lines
---
 Makefile                    |    7 +
 arch/Kconfig                |    6 +
 scripts/Makefile            |    1 +
 scripts/Makefile.build      |   15 ++-
 scripts/update_jump_label.c |  335 +++++++++++++++++++++++++++++++++++++++++++
 scripts/update_jump_label.h |  283 ++++++++++++++++++++++++++++++++++++
 6 files changed, 645 insertions(+), 2 deletions(-)
 create mode 100644 scripts/update_jump_label.c
 create mode 100644 scripts/update_jump_label.h

[...]

Quoted 49 lines
scripts/update_jump_label.c new file +43 -0
new file mode 100644
index 0000000..399d898
@@ -0,0 +1,335 @@
+/*
+ * update_jump_label.c: replace jmps with nops at compile time.
+ * Copyright 2010 Steven Rostedt <[email protected]>, Red Hat Inc.
+ *  Parsing of the elf file was influenced by recordmcount.c
+ *  originally written by and copyright to John F. Reiser <[email protected]>.
+ */
+
+/*
+ * Note, this code is originally designed for x86, but may be used by
+ * other archs to do the nop updates at compile time instead of at boot time.
+ * X86 uses this as an optimization, as jmps can be either 2 bytes or 5 bytes.
+ * Inserting a 2 byte where possible helps with both CPU performance and
+ * icache strain.
+ */
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <getopt.h>
+#include <elf.h>
+#include <fcntl.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+
+static int fd_map;	/* File descriptor for file being modified. */
+static struct stat sb;	/* Remember .st_size, etc. */
+static int mmap_failed; /* Boolean flag. */
+
+static void die(const char *err, const char *fmt, ...)
+{
+	va_list ap;
+
+	if (err)
+		perror(err);
+
+	if (fmt) {
+		va_start(ap, fmt);
+		fprintf(stderr, "Fatal error:  ");
+		vfprintf(stderr, fmt, ap);
+		fprintf(stderr, "\n");

scripts/update_jump_label.c line 53

Quoted 10 lines
scripts/update_jump_label.c new file +10 -0
+		va_end(ap);
+	}
+
+	exit(1);
+}
+
+static void usage(char **argv)
+{
+	char *arg = argv[0];
+	char *p = arg+strlen(arg);

I'm pretty sure checkpatch will complain about the coding style of this file. I won't point out the various nits, but there is a need for cleanup before pulling it.

Quoted 9 lines
scripts/update_jump_label.c new file +9 -0
+
+	while (p >= arg && *p != '/')
+		p--;
+	p++;
+
+	printf("usage: %s file\n"
+	       "\n", p);
+	exit(-1);
+}

[...]

Quoted 3 lines

+/*
+ * Read the relocation table again, but this time its called on sections
+ * that are not going to be traced. The mcount calls here will be converted

mcount calls -> jump labels

Quoted 20 lines

+ * into nops.
+ */

scripts/update_jump_label.c new file +18 -0
+static void FUNC(nop_jump_label)(Elf_Shdr const *const relhdr,
+		       Elf_Ehdr const *const ehdr,
+		       const char *const txtname)
+{
+	Elf_Shdr *const shdr0 = get_shdr(ehdr);
+	Elf_Sym const *sym0;
+	char const *str0;
+	Elf_Rel const *relp;
+	Elf_Rela const *relap;
+	Elf_Shdr const *const shdr = &shdr0[w(relhdr->sh_info)];
+	unsigned rel_entsize = w(relhdr->sh_entsize);
+	unsigned const nrel = _w(relhdr->sh_size) / rel_entsize;
+	int t;
+
+	FUNC(get_sym_str_and_relp)(relhdr, ehdr, &sym0, &str0, &relp);
+
+	for (t = nrel; t > 0; t -= 3) {
+		int ret = -1;

scripts/update_jump_label.c

Quoted 10 lines
scripts/update_jump_label.c new file +10 -0
+
+		relap = (Elf_Rela const *)relp;
+		printf("rel offset=%lx info=%lx sym=%lx type=%lx addend=%lx\n",
+		       (long)relap->r_offset, (long)relap->r_info,
+		       (long)ELF_R_SYM(relap->r_info),
+		       (long)ELF_R_TYPE(relap->r_info),
+		       (long)relap->r_addend);
+
+		if (0 && make_nop)
+			ret = make_nop((void *)ehdr, shdr->sh_offset + relp->r_offset);

if (0 && -> leftover code ?

This whole FUNC(nop_jump_label) function seems to be unused.

Quoted 73 lines
scripts/update_jump_label.c new file +73 -0
+
+		/* jump label sections are paired in threes */
+		relp = (Elf_Rel const *)(rel_entsize * 3 + (void *)relp);
+	}
+}
+
+/* Find relocation section hdr for a given section */
+static const Elf_Shdr *
+FUNC(find_relhdr)(const Elf_Ehdr *ehdr, const Elf_Shdr *shdr)
+{
+	const Elf_Shdr *shdr0 = get_shdr(ehdr);
+	int nhdr = w2(ehdr->e_shnum);
+	const Elf_Shdr *hdr;
+	int i;
+
+	for (hdr = shdr0, i = 0; i < nhdr; hdr = &shdr0[++i]) {
+		if (w(hdr->sh_type) != SHT_REL &&
+		    w(hdr->sh_type) != SHT_RELA)
+			continue;
+
+		/*
+		 * The relocation section's info field holds
+		 * the section index that it represents.
+		 */
+		if (shdr == &shdr0[w(hdr->sh_info)])
+			return hdr;
+	}
+	return NULL;
+}
+
+/* Find a section headr based on name and type */
+static const Elf_Shdr *
+FUNC(find_shdr)(const Elf_Ehdr *ehdr, const char *name, uint_t type)
+{
+	const Elf_Shdr *shdr0 = get_shdr(ehdr);
+	const Elf_Shdr *shstr = &shdr0[w2(ehdr->e_shstrndx)];
+	const char *shstrtab = (char *)get_section_loc(ehdr, shstr);
+	int nhdr = w2(ehdr->e_shnum);
+	const Elf_Shdr *hdr;
+	const char *hdrname;
+	int i;
+
+	for (hdr = shdr0, i = 0; i < nhdr; hdr = &shdr0[++i]) {
+		if (w(hdr->sh_type) != type)
+			continue;
+
+		/* If we are just looking for a section by type (ie. SYMTAB) */
+		if (!name)
+			return hdr;
+
+		hdrname = &shstrtab[w(hdr->sh_name)];
+		if (strcmp(hdrname, name) == 0)
+			return hdr;
+	}
+	return NULL;
+}
+
+static void
+FUNC(section_update)(const Elf_Ehdr *ehdr, const Elf_Shdr *symhdr,
+		     unsigned shtype, const Elf_Rel *rel, void *data)
+{
+	const Elf_Shdr *shdr0 = get_shdr(ehdr);
+	const Elf_Shdr *targethdr;
+	const Elf_Rela *rela;
+	const Elf_Sym *syment;
+	uint_t offset = _w(rel->r_offset);
+	uint_t info = _w(rel->r_info);
+	uint_t sym = ELF_R_SYM(info);
+	uint_t type = ELF_R_TYPE(info);
+	uint_t addend;
+	uint_t targetloc;
+
+	if (shtype == SHT_RELA) {

scripts/update_jump_label.c

Quoted 10 lines
scripts/update_jump_label.c new file +10 -0
+		rela = (const Elf_Rela *)rel;
+		addend = _w(rela->r_addend);
+	} else
+		addend = _w(*(int *)(data + offset));
+
+	syment = (const Elf_Sym *)get_section_loc(ehdr, symhdr);
+	targethdr = &shdr0[w2(syment[sym].st_shndx)];
+	targetloc = _w(targethdr->sh_offset);
+
+	/* TODO, need a separate function for all archs */

maybe this is where you want to call FUNC(nop_jump_label) too ?

Quoted 63 lines
scripts/update_jump_label.c new file +63 -0
+	if (type != R_386_32)
+		die(NULL, "Arch relocation type %d not supported", type);
+
+	targetloc += addend;
+
+#if 0
+	printf("offset=%x target=%x shoffset=%x add=%x indx=%x\n",
+	       offset, targetloc, _w(targethdr->sh_offset), addend,
+	       targetloc - _w(targethdr->sh_offset));
+#endif
+	*(uint_t *)(data + offset) = targetloc;
+}
+
+/* Overall supervision for Elf32 ET_REL file. */
+static void
+FUNC(do_func)(Elf_Ehdr *ehdr, char const *const fname, unsigned const reltype)
+{
+	const Elf_Shdr *jlshdr;
+	const Elf_Shdr *jlrhdr;
+	const Elf_Shdr *symhdr;
+	const Elf_Rel *rel;
+	unsigned size;
+	unsigned cnt;
+	unsigned i;
+	uint_t type;
+	void *jdata;
+	void *data;
+
+	jlshdr = FUNC(find_shdr)(ehdr, "__jump_table", SHT_PROGBITS);
+	if (!jlshdr)
+		return;
+
+	jlrhdr = FUNC(find_relhdr)(ehdr, jlshdr);
+	if (!jlrhdr)
+		return;
+
+	/*
+	 * Create and fill in the __jump_table section and use it to
+	 * find the offsets into the text that we want to update.
+	 * We create it so that we do not depend on the order of the
+	 * relocations, and use the table directly, as it is broken
+	 * up into sections.
+	 */
+	size = _w(jlshdr->sh_size);
+	data = umalloc(size);
+
+	jdata = (void *)get_section_loc(ehdr, jlshdr);
+	memcpy(data, jdata, size);
+
+	cnt = _w(jlrhdr->sh_size) / w(jlrhdr->sh_entsize);
+
+	rel = (const Elf_Rel *)get_section_loc(ehdr, jlrhdr);
+
+	/* Is this as Rel or Rela? */
+	type = w(jlrhdr->sh_type);
+
+	symhdr = FUNC(find_shdr)(ehdr, NULL, SHT_SYMTAB);
+
+	for (i = 0; i < cnt; i++) {
+		FUNC(section_update)(ehdr, symhdr, type, rel, data);
+		rel = (void *)rel + w(jlrhdr->sh_entsize);
+	}
+

scripts/update_jump_label.c

Quoted 10 lines
scripts/update_jump_label.c new file +10 -0
+	/*
+	 * This is specific to x86. The jump_table is stored in three
+	 * long words. The first is the location of the jmp target we
+	 * must update.
+	 */
+	cnt = size / sizeof(uint_t);
+
+	for (i = 0; i < cnt; i += 3)
+		make_nop((void *)ehdr, *(uint_t *)(data + i * sizeof(uint_t)));
+

Why is this function iterating twice on the __jump_table section rather than simply writing the nops in the first pass, within the section_update function ?

Thanks,

Mathieu

Quoted 6 lines
scripts/update_jump_label.c new file +2 -0
+	free(data);
+}
Signature · 1 line
1.7.8.3
Signature · 4 lines
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
author reviewer
#3 lore raw

Re: [PATCH 1/2] jump labels: Add infrastructure to update jump labels at compile time

Quoted 7 lines · Mathieu Desnoyers

Hi Steven,

I really like this patchset! A few comments below,

Quoted 2 lines · * Steven Rostedt ([email protected]) wrote:

From: Steven Rostedt <[email protected]>

Quoted 7 lines
Quoted 2 lines

Add the infrastructure to allow architectures to modify the jump label locations at compile time. This is mainly for x86, where the jmps may

is it "at compile time" or "after compilation" ? AFAIU, this update_jump_label script gets executed on the .o, which makes it technically more part of the linking stage than the compilation stage.

We usually denote things as "at compile time" or "at run time" whether it is the linker or not, we don't differentiate because it's not important. The "at compile time" means, it happens once before you boot the kernel, as suppose to, it happens every time your run the kernel. That's the important part.

Quoted 6 lines
Quoted 6 lines

be either 2 bytes or 5 bytes. Instead of wasting 5 bytes for all jump labels, this code will let x86 put in a jmp instead of a 5 byte nop. Then the assembler will make either a 2 byte or 5 byte jmp depending on where the target is.

Quoted 8 lines
Quoted 3 lines

At compile time, this code will replace the jmps with either a 2 byte or 5 byte nop depending on the size of the jmp that was added.

A small note somewhere saying that this 2 vs 5 bytes choice done by the compiler is a pessimistic approximation would be good, so people don't end up complaining if they see that the compiler chose a 5-byte nop for a 120 bytes offset.

Hmm, that really doesn't have anything to do with this patch. If someone is anal enough to complain, then they can complain to the gcc people. As it would do the same with other jmps not associated with jumplabels too.

Quoted 69 lines
Quoted 13 lines
---
 Makefile                    |    7 +
 arch/Kconfig                |    6 +
 scripts/Makefile            |    1 +
 scripts/Makefile.build      |   15 ++-
 scripts/update_jump_label.c |  335 +++++++++++++++++++++++++++++++++++++++++++
 scripts/update_jump_label.h |  283 ++++++++++++++++++++++++++++++++++++
 6 files changed, 645 insertions(+), 2 deletions(-)
 create mode 100644 scripts/update_jump_label.c
 create mode 100644 scripts/update_jump_label.h

[...]

Quoted 55 lines
scripts/update_jump_label.c new file +49 -0
new file mode 100644
index 0000000..399d898
@@ -0,0 +1,335 @@
+/*
+ * update_jump_label.c: replace jmps with nops at compile time.
+ * Copyright 2010 Steven Rostedt <[email protected]>, Red Hat Inc.
+ *  Parsing of the elf file was influenced by recordmcount.c
+ *  originally written by and copyright to John F. Reiser <[email protected]>.
+ */
+
+/*
+ * Note, this code is originally designed for x86, but may be used by
+ * other archs to do the nop updates at compile time instead of at boot time.
+ * X86 uses this as an optimization, as jmps can be either 2 bytes or 5 bytes.
+ * Inserting a 2 byte where possible helps with both CPU performance and
+ * icache strain.
+ */
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <getopt.h>
+#include <elf.h>
+#include <fcntl.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+
+static int fd_map;	/* File descriptor for file being modified. */
+static struct stat sb;	/* Remember .st_size, etc. */
+static int mmap_failed; /* Boolean flag. */
+
+static void die(const char *err, const char *fmt, ...)
+{
+	va_list ap;
+
+	if (err)
+		perror(err);
+
+	if (fmt) {
+		va_start(ap, fmt);
+		fprintf(stderr, "Fatal error:  ");
+		vfprintf(stderr, fmt, ap);
+		fprintf(stderr, "\n");
+		va_end(ap);
+	}
+
+	exit(1);
+}
+
Quoted 8 lines

scripts/update_jump_label.c line 53

Quoted 4 lines
scripts/update_jump_label.c new file +4 -0
+static void usage(char **argv)
+{
+	char *arg = argv[0];
+	char *p = arg+strlen(arg);

I'm pretty sure checkpatch will complain about the coding style of this file. I won't point out the various nits, but there is a need for cleanup before pulling it.

Yeah, this code was based off of the record_mcount.c file. I need to clean that up too. And not just a trivial clean up, but a complete overhaul. It's had a couple already. The guy who wrote the original code use some really strange coding conventions and was really hard to read and understand. I spent days cleaning it up to make it presentable for Linux. I now know this code enough to make another pass at it and make it a bit more readable.

I can fix this little thing up now.

Quoted 13 lines
Quoted 10 lines
scripts/update_jump_label.c new file +9 -0
+
+	while (p >= arg && *p != '/')
+		p--;
+	p++;
+
+	printf("usage: %s file\n"
+	       "\n", p);
+	exit(-1);
+}

[...]

Quoted 5 lines
Quoted 3 lines

+/*
+ * Read the relocation table again, but this time its called on sections
+ * that are not going to be traced. The mcount calls here will be converted

mcount calls -> jump labels

Hehe, like I said, this was based off of the record_mcount code. I did state that in the original code. Thanks, I'll fix this too.

Quoted 34 lines
Quoted 21 lines

+ * into nops.
+ */

scripts/update_jump_label.c new file +18 -0
+static void FUNC(nop_jump_label)(Elf_Shdr const *const relhdr,
+		       Elf_Ehdr const *const ehdr,
+		       const char *const txtname)
+{
+	Elf_Shdr *const shdr0 = get_shdr(ehdr);
+	Elf_Sym const *sym0;
+	char const *str0;
+	Elf_Rel const *relp;
+	Elf_Rela const *relap;
+	Elf_Shdr const *const shdr = &shdr0[w(relhdr->sh_info)];
+	unsigned rel_entsize = w(relhdr->sh_entsize);
+	unsigned const nrel = _w(relhdr->sh_size) / rel_entsize;
+	int t;
+
+	FUNC(get_sym_str_and_relp)(relhdr, ehdr, &sym0, &str0, &relp);
+
+	for (t = nrel; t > 0; t -= 3) {
+		int ret = -1;

scripts/update_jump_label.c

Quoted 10 lines
scripts/update_jump_label.c new file +10 -0
+
+		relap = (Elf_Rela const *)relp;
+		printf("rel offset=%lx info=%lx sym=%lx type=%lx addend=%lx\n",
+		       (long)relap->r_offset, (long)relap->r_info,
+		       (long)ELF_R_SYM(relap->r_info),
+		       (long)ELF_R_TYPE(relap->r_info),
+		       (long)relap->r_addend);
+
+		if (0 && make_nop)
+			ret = make_nop((void *)ehdr, shdr->sh_offset + relp->r_offset);

if (0 && -> leftover code ?

Quoted 1 line

This whole FUNC(nop_jump_label) function seems to be unused.

Yeah, I still need to remove this crap. I posted this as an RFC to get ideas before I ripped out all the debug (and crap). This was code that I wasn't sure I would use. Yes, it will be removed before the final version. I probably can nuke it now, as I'm now comfortable that this will not be needed.

Quoted 78 lines
Quoted 78 lines
scripts/update_jump_label.c new file +77 -0
+
+		/* jump label sections are paired in threes */
+		relp = (Elf_Rel const *)(rel_entsize * 3 + (void *)relp);
+	}
+}
+
+/* Find relocation section hdr for a given section */
+static const Elf_Shdr *
+FUNC(find_relhdr)(const Elf_Ehdr *ehdr, const Elf_Shdr *shdr)
+{
+	const Elf_Shdr *shdr0 = get_shdr(ehdr);
+	int nhdr = w2(ehdr->e_shnum);
+	const Elf_Shdr *hdr;
+	int i;
+
+	for (hdr = shdr0, i = 0; i < nhdr; hdr = &shdr0[++i]) {
+		if (w(hdr->sh_type) != SHT_REL &&
+		    w(hdr->sh_type) != SHT_RELA)
+			continue;
+
+		/*
+		 * The relocation section's info field holds
+		 * the section index that it represents.
+		 */
+		if (shdr == &shdr0[w(hdr->sh_info)])
+			return hdr;
+	}
+	return NULL;
+}
+
+/* Find a section headr based on name and type */
+static const Elf_Shdr *
+FUNC(find_shdr)(const Elf_Ehdr *ehdr, const char *name, uint_t type)
+{
+	const Elf_Shdr *shdr0 = get_shdr(ehdr);
+	const Elf_Shdr *shstr = &shdr0[w2(ehdr->e_shstrndx)];
+	const char *shstrtab = (char *)get_section_loc(ehdr, shstr);
+	int nhdr = w2(ehdr->e_shnum);
+	const Elf_Shdr *hdr;
+	const char *hdrname;
+	int i;
+
+	for (hdr = shdr0, i = 0; i < nhdr; hdr = &shdr0[++i]) {
+		if (w(hdr->sh_type) != type)
+			continue;
+
+		/* If we are just looking for a section by type (ie. SYMTAB) */
+		if (!name)
+			return hdr;
+
+		hdrname = &shstrtab[w(hdr->sh_name)];
+		if (strcmp(hdrname, name) == 0)
+			return hdr;
+	}
+	return NULL;
+}
+
+static void
+FUNC(section_update)(const Elf_Ehdr *ehdr, const Elf_Shdr *symhdr,
+		     unsigned shtype, const Elf_Rel *rel, void *data)
+{
+	const Elf_Shdr *shdr0 = get_shdr(ehdr);
+	const Elf_Shdr *targethdr;
+	const Elf_Rela *rela;
+	const Elf_Sym *syment;
+	uint_t offset = _w(rel->r_offset);
+	uint_t info = _w(rel->r_info);
+	uint_t sym = ELF_R_SYM(info);
+	uint_t type = ELF_R_TYPE(info);
+	uint_t addend;
+	uint_t targetloc;
+
+	if (shtype == SHT_RELA) {
+		rela = (const Elf_Rela *)rel;
+		addend = _w(rela->r_addend);
+	} else
+		addend = _w(*(int *)(data + offset));
Quoted 8 lines

scripts/update_jump_label.c

Quoted 6 lines
scripts/update_jump_label.c new file +6 -0
+
+	syment = (const Elf_Sym *)get_section_loc(ehdr, symhdr);
+	targethdr = &shdr0[w2(syment[sym].st_shndx)];
+	targetloc = _w(targethdr->sh_offset);
+
+	/* TODO, need a separate function for all archs */

maybe this is where you want to call FUNC(nop_jump_label) too ?

It would be where something similar goes, but not that function. I should still nuke it.

Quoted 11 lines

scripts/update_jump_label.c

Quoted 11 lines
scripts/update_jump_label.c new file +10 -0
+	if (type != R_386_32)
+		die(NULL, "Arch relocation type %d not supported", type);
+
+	targetloc += addend;
+
+#if 0
+	printf("offset=%x target=%x shoffset=%x add=%x indx=%x\n",
+	       offset, targetloc, _w(targethdr->sh_offset), addend,
+	       targetloc - _w(targethdr->sh_offset));
+#endif

This obviously needs to be nuked too.

Quoted 59 lines
Quoted 59 lines
scripts/update_jump_label.c new file +59 -0
+	*(uint_t *)(data + offset) = targetloc;
+}
+
+/* Overall supervision for Elf32 ET_REL file. */
+static void
+FUNC(do_func)(Elf_Ehdr *ehdr, char const *const fname, unsigned const reltype)
+{
+	const Elf_Shdr *jlshdr;
+	const Elf_Shdr *jlrhdr;
+	const Elf_Shdr *symhdr;
+	const Elf_Rel *rel;
+	unsigned size;
+	unsigned cnt;
+	unsigned i;
+	uint_t type;
+	void *jdata;
+	void *data;
+
+	jlshdr = FUNC(find_shdr)(ehdr, "__jump_table", SHT_PROGBITS);
+	if (!jlshdr)
+		return;
+
+	jlrhdr = FUNC(find_relhdr)(ehdr, jlshdr);
+	if (!jlrhdr)
+		return;
+
+	/*
+	 * Create and fill in the __jump_table section and use it to
+	 * find the offsets into the text that we want to update.
+	 * We create it so that we do not depend on the order of the
+	 * relocations, and use the table directly, as it is broken
+	 * up into sections.
+	 */
+	size = _w(jlshdr->sh_size);
+	data = umalloc(size);
+
+	jdata = (void *)get_section_loc(ehdr, jlshdr);
+	memcpy(data, jdata, size);
+
+	cnt = _w(jlrhdr->sh_size) / w(jlrhdr->sh_entsize);
+
+	rel = (const Elf_Rel *)get_section_loc(ehdr, jlrhdr);
+
+	/* Is this as Rel or Rela? */
+	type = w(jlrhdr->sh_type);
+
+	symhdr = FUNC(find_shdr)(ehdr, NULL, SHT_SYMTAB);
+
+	for (i = 0; i < cnt; i++) {
+		FUNC(section_update)(ehdr, symhdr, type, rel, data);
+		rel = (void *)rel + w(jlrhdr->sh_entsize);
+	}
+
+	/*
+	 * This is specific to x86. The jump_table is stored in three
+	 * long words. The first is the location of the jmp target we
+	 * must update.
+	 */
+	cnt = size / sizeof(uint_t);
Quoted 8 lines

scripts/update_jump_label.c

Quoted 4 lines
scripts/update_jump_label.c new file +4 -0
+
+	for (i = 0; i < cnt; i += 3)
+		make_nop((void *)ehdr, *(uint_t *)(data + i * sizeof(uint_t)));
+

Why is this function iterating twice on the __jump_table section rather than simply writing the nops in the first pass, within the section_update function ?

To simplify things. There's also no guarantee that the relocation table is in order. It usually is, but I don't want to assume that. Thus I need to first read the entire relocation table to find the locations of all the jump labels, and fill in the locations as they would have been linked. And after that is complete, then I can iterate over that section and look at every third entry which is the jump label location.

-- Steve

author reviewer
#4 lore raw

PATCH 2/2 jump labels/x86: Use etiher 5 byte or 2 byte jumps

In reply to Steven Rostedt

From: Steven Rostedt <[email protected]>

Have the jump labels add a "jmp" in the assembly instead of a default nop. This will cause the assembler to put in either a 2 byte or 5 byte jmp depending on where the target lable is.

Then at compile time, the update_jump_label code will replace the jmps with either 2 or 5 byte nops.

On boot up, the code can be examined to see if the jump label uses either a 2 or 5 byte nop and replace it.

By allowing the jump labels to be 2 bytes, it speeds up the nops, not only 2 byte nops are faster than 5 byte nops, but also because it saves on cache foot print.

text data bss dec hex filename
13403667 3666856 2998272 20068795 13239bb ../nobackup/mxtest/vmlinux-old 13398536 3666856 2998272 20063664 13225b0 ../nobackup/mxtest/vmlinux-new

Converting the current v3.2 trace points saved 5,131 bytes. As more places use jump labels, this will have a bigger savings.

---
 arch/x86/Kconfig                  |    1 +
 arch/x86/include/asm/jump_label.h |    2 +-
 arch/x86/kernel/jump_label.c      |   86 ++++++++++++++++++++++++++++++++++---
 3 files changed, 81 insertions(+), 8 deletions(-)
arch/x86/Kconfig +1 -0
index efb4294..b5004c1 100644
@@ -61,6 +61,7 @@ config X86
 	select HAVE_ARCH_KMEMCHECK
 	select HAVE_USER_RETURN_NOTIFIER
 	select HAVE_ARCH_JUMP_LABEL
+	select HAVE_BUILD_TIME_JUMP_LABEL
 	select HAVE_TEXT_POKE_SMP
 	select HAVE_GENERIC_HARDIRQS
 	select HAVE_SPARSE_IRQ
arch/x86/include/asm/jump_label.h +1 -1
index a32b18c..872b3e1 100644
@@ -14,7 +14,7 @@
 static __always_inline bool arch_static_branch(struct jump_label_key *key)
 {
 	asm goto("1:"
-		JUMP_LABEL_INITIAL_NOP
+		"jmp %l[l_yes]\n"
 		".pushsection __jump_table,  \"aw\" \n\t"
 		_ASM_ALIGN "\n\t"
 		_ASM_PTR "1b, %l[l_yes], %c0 \n\t"
arch/x86/kernel/jump_label.c +79 -7

117 lines not on this page · raw message

Signature · 1 line
1.7.8.3
#5 lore raw

Re: [PATCH 2/2] jump labels/x86: Use etiher 5 byte or 2 byte jumps

Quoted 17 lines · Steven Rostedt

From: Steven Rostedt <[email protected]>

Have the jump labels add a "jmp" in the assembly instead of a default nop. This will cause the assembler to put in either a 2 byte or 5 byte jmp depending on where the target lable is.

Then at compile time, the update_jump_label code will replace the jmps with either 2 or 5 byte nops.

On boot up, the code can be examined to see if the jump label uses either a 2 or 5 byte nop and replace it.

By allowing the jump labels to be 2 bytes, it speeds up the nops, not only 2 byte nops are faster than 5 byte nops, but also because it saves on cache foot print.

Quoted 6 lines

text data bss dec hex filename
13403667 3666856 2998272 20068795 13239bb ../nobackup/mxtest/vmlinux-old 13398536 3666856 2998272 20063664 13225b0 ../nobackup/mxtest/vmlinux-new

Converting the current v3.2 trace points saved 5,131 bytes. As more places use jump labels, this will have a bigger savings.

Yummie!

This is very nice, as that's 5K saved from rather hot codepaths quite often, so this feature increases the effective instruction cache size of the CPU in essence.

Thanks,

Ingo

#6 lore raw

Re: [PATCH 2/2] jump labels/x86: Use etiher 5 byte or 2 byte jumps

In reply to Steven Rostedt
Quoted 48 lines · * Steven Rostedt ([email protected]) wrote:

From: Steven Rostedt <[email protected]>

Have the jump labels add a "jmp" in the assembly instead of a default nop. This will cause the assembler to put in either a 2 byte or 5 byte jmp depending on where the target lable is.

Then at compile time, the update_jump_label code will replace the jmps with either 2 or 5 byte nops.

On boot up, the code can be examined to see if the jump label uses either a 2 or 5 byte nop and replace it.

By allowing the jump labels to be 2 bytes, it speeds up the nops, not only 2 byte nops are faster than 5 byte nops, but also because it saves on cache foot print.

text data bss dec hex filename
13403667 3666856 2998272 20068795 13239bb ../nobackup/mxtest/vmlinux-old 13398536 3666856 2998272 20063664 13225b0 ../nobackup/mxtest/vmlinux-new

Converting the current v3.2 trace points saved 5,131 bytes. As more places use jump labels, this will have a bigger savings.

---
 arch/x86/Kconfig                  |    1 +
 arch/x86/include/asm/jump_label.h |    2 +-
 arch/x86/kernel/jump_label.c      |   86 ++++++++++++++++++++++++++++++++++---
 3 files changed, 81 insertions(+), 8 deletions(-)
arch/x86/Kconfig +1 -0
index efb4294..b5004c1 100644
@@ -61,6 +61,7 @@ config X86
 	select HAVE_ARCH_KMEMCHECK
 	select HAVE_USER_RETURN_NOTIFIER
 	select HAVE_ARCH_JUMP_LABEL
+	select HAVE_BUILD_TIME_JUMP_LABEL
 	select HAVE_TEXT_POKE_SMP
 	select HAVE_GENERIC_HARDIRQS
 	select HAVE_SPARSE_IRQ
arch/x86/include/asm/jump_label.h +0 -0
index a32b18c..872b3e1 100644

arch/x86/include/asm/jump_label.h line 17

Quoted 5 lines
arch/x86/include/asm/jump_label.h +1 -1
@@ -14,7 +14,7 @@
 static __always_inline bool arch_static_branch(struct jump_label_key *key)
 {
 	asm goto("1:"
-		JUMP_LABEL_INITIAL_NOP
+		"jmp %l[l_yes]\n"

Is it possible that the compiler choose a jump that is not 2 or 5-byte ? e.g. a jmp rel16 (e9 opcode) on 32-bit x86, or any of the other instruction listed under the JMP-Jump instruction in the Intel insn manual ?

Quoted 37 lines
arch/x86/include/asm/jump_label.h +0 -0
 		".pushsection __jump_table,  \"aw\" \n\t"
 		_ASM_ALIGN "\n\t"
 		_ASM_PTR "1b, %l[l_yes], %c0 \n\t"
arch/x86/kernel/jump_label.c +16 -1
index ea9d5f2f..d5b84de 100644
@@ -16,12 +16,27 @@
 
 #ifdef HAVE_JUMP_LABEL
 
+static unsigned char nop_short[] = { P6_NOP2 };
+
+/* These are the nops added at compile time */
+#ifdef CONFIG_X86_32
+static unsigned char default_nop[5] = { 0x3e, 0x8d, 0x74, 0x26, 0x00 };
+#else
+static unsigned char default_nop[5] = { 0x0f, 0x1f, 0x44, 0x00, 0x00 };
+#endif
+
+static int update_nops;
+
 union jump_code_union {
 	char code[JUMP_LABEL_NOP_SIZE];
 	struct {
 		char jump;
 		int offset;
-	} __attribute__((packed));
+	} __packed;
+	struct {
+		char jump_short;
+		char offset_short;
+	} __packed;
 };
 
 static void __jump_label_transform(struct jump_entry *entry,

arch/x86/kernel/jump_label.c static void __jump_label_transform(struct jump_entry *entry, · line 51

Quoted 8 lines
arch/x86/kernel/jump_label.c +5 -0
@@ -29,20 +44,70 @@ static void __jump_label_transform(struct jump_entry *entry,
 				   void *(*poker)(void *, const void *, size_t))
 {
 	union jump_code_union code;
+	unsigned char nop;
+	unsigned char op;
+	unsigned size;
+	void *ip = (void *)entry->code;
+	void *ideal = (void *)ideal_nops[NOP_ATOMIC5];

"void *" should possibly be "unsigned char *" here to respect the nop place-holder typing.

arch/x86/kernel/jump_label.c static void __jump_label_transform(struct jump_entry *entry, · line 59

Quoted 13 lines
arch/x86/kernel/jump_label.c +6 -5
+
+	/* Use probe_kernel_read()? */
+	op = *(unsigned char *)ip;
+	nop = ideal_nops[NOP_ATOMIC5][0];
 
 	if (type == JUMP_LABEL_ENABLE) {
-		code.jump = 0xe9;
-		code.offset = entry->target -
-				(entry->code + JUMP_LABEL_NOP_SIZE);
-	} else
-		memcpy(&code, ideal_nops[NOP_ATOMIC5], JUMP_LABEL_NOP_SIZE);
+		if (op == 0xe9 || op == 0xeb)
+			/* Already enabled. Warn? */

This could be caused by failure to run the link-time script, or running the transform twice. A warning would indeed be welcome, as this should never happen.

Quoted 20 lines
arch/x86/kernel/jump_label.c +18 -1
+			return;
+
+		if (memcmp(ip, nop_short, 2) == 0) {
+			size = 2;
+			code.jump_short = 0xeb;
+			code.offset = entry->target -
+				(entry->code + 2);
+			/* Check for overflow ? */
+		} else if (memcmp(ip, ideal, 5) == 0 ||
+			   memcmp(ip, default_nop, 5)) {
+			size = JUMP_LABEL_NOP_SIZE;
+			code.jump = 0xe9;
+			code.offset = entry->target - (entry->code + size);
+		} else
+			BUG();
 
-	(*poker)((void *)entry->code, &code, JUMP_LABEL_NOP_SIZE);
+	} else {
+		/* Check if already disabled */
+		if (memcmp(ip, nop_short, 2) == 0)

arch/x86/kernel/jump_label.c static void __jump_label_transform(struct jump_entry *entry, · line 88

Quoted 10 lines
arch/x86/kernel/jump_label.c +10 -0
+			return;
+
+		if (memcmp(ip, ideal, 5) == 0)
+			return;
+
+		/* This may need to update from default to ideal */
+		if (update_nops && memcmp(ip, default_nop, 5) == 0) {
+			/* Set to the ideal nop */
+			size = JUMP_LABEL_NOP_SIZE;
+			memcpy(&code, ideal_nops[NOP_ATOMIC5], size);

whiteline.

arch/x86/kernel/jump_label.c static void __jump_label_transform(struct jump_entry *entry, · line 100

Quoted 12 lines
arch/x86/kernel/jump_label.c +12 -0
+
+		} else if (op == 0xe9) {
+			/* Replace a 5 byte jmp */
+			size = JUMP_LABEL_NOP_SIZE;
+			memcpy(&code, ideal_nops[NOP_ATOMIC5], size);
+		} else if (op == 0xeb) {
+			/* Replace a 2 byte jmp */
+			size = 2;
+			memcpy(&code, nop_short, size);
+		} else
+			BUG();
+	}

Thanks,

Mathieu

Quoted 31 lines
arch/x86/kernel/jump_label.c +12 -0
+
+	(*poker)((void *)entry->code, &code, size);
 }
 
 void arch_jump_label_transform(struct jump_entry *entry,
 			       enum jump_label_type type)
 {
+	/* All nops should be updated to the ideal nop by now */
+	update_nops = 0;
+
 	get_online_cpus();
 	mutex_lock(&text_mutex);
 	__jump_label_transform(entry, type, text_poke_smp);
@@ -53,6 +118,13 @@ void arch_jump_label_transform(struct jump_entry *entry,
 void arch_jump_label_transform_static(struct jump_entry *entry,
 				      enum jump_label_type type)
 {
+	/*
+	 * If the default nop does not equal the ideal nop, then
+	 * update them.
+	 */
+	if (memcmp(default_nop, ideal_nops[NOP_ATOMIC5], 5) != 0)
+		update_nops = 1;
+
 	__jump_label_transform(entry, type, text_poke_early);
 }
 
Signature · 1 line
1.7.8.3
Signature · 4 lines
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
#7 lore raw

Re: [PATCH 2/2] jump labels/x86: Use etiher 5 byte or 2 byte jumps

Quoted 7 lines · Mathieu Desnoyers
Quoted 7 lines
arch/x86/include/asm/jump_label.h +0 -0
index a32b18c..872b3e1 100644
@@ -14,7 +14,7 @@
 static __always_inline bool arch_static_branch(struct jump_label_key *key)
 {
Quoted 9 lines

arch/x86/include/asm/jump_label.h line 17

Quoted 3 lines
arch/x86/include/asm/jump_label.h +1 -1
 	asm goto("1:"
-		JUMP_LABEL_INITIAL_NOP
+		"jmp %l[l_yes]\n"

Is it possible that the compiler choose a jump that is not 2 or 5-byte ? e.g. a jmp rel16 (e9 opcode) on 32-bit x86, or any of the other instruction listed under the JMP-Jump instruction in the Intel insn manual ?

No.

Quoted 12 lines

arch/x86/include/asm/jump_label.h

Quoted 8 lines
arch/x86/include/asm/jump_label.h +5 -0
 				   void *(*poker)(void *, const void *, size_t))
 {
 	union jump_code_union code;
+	unsigned char nop;
+	unsigned char op;
+	unsigned size;
+	void *ip = (void *)entry->code;
+	void *ideal = (void *)ideal_nops[NOP_ATOMIC5];

"void *" should possibly be "unsigned char *" here to respect the nop place-holder typing.

const unsigned char * please.

Quoted 5 lines
Quoted 5 lines
arch/x86/include/asm/jump_label.h +4 -0
+
+	/* Use probe_kernel_read()? */
+	op = *(unsigned char *)ip;
+	nop = ideal_nops[NOP_ATOMIC5][0];
Quoted 13 lines

arch/x86/include/asm/jump_label.h

Quoted 8 lines
arch/x86/include/asm/jump_label.h +2 -5
 	if (type == JUMP_LABEL_ENABLE) {
-		code.jump = 0xe9;
-		code.offset = entry->target -
-				(entry->code + JUMP_LABEL_NOP_SIZE);
-	} else
-		memcpy(&code, ideal_nops[NOP_ATOMIC5], JUMP_LABEL_NOP_SIZE);
+		if (op == 0xe9 || op == 0xeb)
+			/* Already enabled. Warn? */

This could be caused by failure to run the link-time script, or running the transform twice. A warning would indeed be welcome, as this should never happen.

Warning? No. ERROR. Something very bad could be happening here. We have covered this before.

-hpa

Signature · 2 lines
H. Peter Anvin, Intel Open Source Technology Center
I work for Intel.  I don't speak on their behalf.
author reviewer
#8 lore raw

Re: [PATCH 2/2] jump labels/x86: Use etiher 5 byte or 2 byte jumps

Quoted 13 lines · H. Peter Anvin
Quoted 12 lines

arch/x86/kernel/jump_label.c static void __jump_label_transform(struct jump_entry *entry, · line 51

Quoted 8 lines
arch/x86/kernel/jump_label.c +5 -0
 				   void *(*poker)(void *, const void *, size_t))
 {
 	union jump_code_union code;
+	unsigned char nop;
+	unsigned char op;
+	unsigned size;
+	void *ip = (void *)entry->code;
+	void *ideal = (void *)ideal_nops[NOP_ATOMIC5];

"void *" should possibly be "unsigned char *" here to respect the nop place-holder typing.

Quoted 1 line

const unsigned char * please.

OK.

Quoted 20 lines
Quoted 6 lines
Quoted 6 lines
arch/x86/kernel/jump_label.c +4 -0
+
+	/* Use probe_kernel_read()? */
+	op = *(unsigned char *)ip;
+	nop = ideal_nops[NOP_ATOMIC5][0];
Quoted 13 lines

arch/x86/kernel/jump_label.c static void __jump_label_transform(struct jump_entry *entry, · line 59

Quoted 8 lines
arch/x86/kernel/jump_label.c +2 -5
 	if (type == JUMP_LABEL_ENABLE) {
-		code.jump = 0xe9;
-		code.offset = entry->target -
-				(entry->code + JUMP_LABEL_NOP_SIZE);
-	} else
-		memcpy(&code, ideal_nops[NOP_ATOMIC5], JUMP_LABEL_NOP_SIZE);
+		if (op == 0xe9 || op == 0xeb)
+			/* Already enabled. Warn? */

This could be caused by failure to run the link-time script, or running the transform twice. A warning would indeed be welcome, as this should never happen.

Quoted 2 lines

Warning? No. ERROR. Something very bad could be happening here. We have covered this before.

Heh, not this exactly. We covered run time errors, this is build time errors.

But I agree, it should error. The mcount code errors on problems, this should too.

-- Steve

author reviewer
#9 lore raw

Re: [PATCH 2/2] jump labels/x86: Use etiher 5 byte or 2 byte jumps

Quoted 22 lines · Steven Rostedt
Quoted 20 lines · H. Peter Anvin
Quoted 19 lines
Quoted 4 lines
arch/x86/kernel/jump_label.c +3 -0
+
+	/* Use probe_kernel_read()? */
+	op = *(unsigned char *)ip;

arch/x86/kernel/jump_label.c static void __jump_label_transform(struct jump_entry *entry, · line 59

Quoted 10 lines
arch/x86/kernel/jump_label.c +3 -5
+	nop = ideal_nops[NOP_ATOMIC5][0];
 
 	if (type == JUMP_LABEL_ENABLE) {
-		code.jump = 0xe9;
-		code.offset = entry->target -
-				(entry->code + JUMP_LABEL_NOP_SIZE);
-	} else
-		memcpy(&code, ideal_nops[NOP_ATOMIC5], JUMP_LABEL_NOP_SIZE);
+		if (op == 0xe9 || op == 0xeb)
+			/* Already enabled. Warn? */

This could be caused by failure to run the link-time script, or running the transform twice. A warning would indeed be welcome, as this should never happen.

Quoted 5 lines
Quoted 2 lines

Warning? No. ERROR. Something very bad could be happening here. We have covered this before.

Heh, not this exactly. We covered run time errors, this is build time errors.

Oops, this is patch 2 not 1. It is run time error.

Quoted 3 lines

But I agree, it should error. The mcount code errors on problems, this should too.

I'll have to look at this in more detail, to make sure jump labels are consistent in their updates. It may legitimately enable them twice. If that's the case, what should be done is to make sure the entire instruction is what we expect it to be, otherwise BUG().

-- Steve

author reviewer
#10 lore raw

Re: [PATCH 2/2] jump labels/x86: Use etiher 5 byte or 2 byte jumps

In reply to Mathieu Desnoyers
Quoted 49 lines · Mathieu Desnoyers
Quoted 48 lines · * Steven Rostedt ([email protected]) wrote:

From: Steven Rostedt <[email protected]>

Have the jump labels add a "jmp" in the assembly instead of a default nop. This will cause the assembler to put in either a 2 byte or 5 byte jmp depending on where the target lable is.

Then at compile time, the update_jump_label code will replace the jmps with either 2 or 5 byte nops.

On boot up, the code can be examined to see if the jump label uses either a 2 or 5 byte nop and replace it.

By allowing the jump labels to be 2 bytes, it speeds up the nops, not only 2 byte nops are faster than 5 byte nops, but also because it saves on cache foot print.

text data bss dec hex filename
13403667 3666856 2998272 20068795 13239bb ../nobackup/mxtest/vmlinux-old 13398536 3666856 2998272 20063664 13225b0 ../nobackup/mxtest/vmlinux-new

Converting the current v3.2 trace points saved 5,131 bytes. As more places use jump labels, this will have a bigger savings.

---
 arch/x86/Kconfig                  |    1 +
 arch/x86/include/asm/jump_label.h |    2 +-
 arch/x86/kernel/jump_label.c      |   86 ++++++++++++++++++++++++++++++++++---
 3 files changed, 81 insertions(+), 8 deletions(-)
arch/x86/Kconfig +1 -0
index efb4294..b5004c1 100644
@@ -61,6 +61,7 @@ config X86
 	select HAVE_ARCH_KMEMCHECK
 	select HAVE_USER_RETURN_NOTIFIER
 	select HAVE_ARCH_JUMP_LABEL
+	select HAVE_BUILD_TIME_JUMP_LABEL
 	select HAVE_TEXT_POKE_SMP
 	select HAVE_GENERIC_HARDIRQS
 	select HAVE_SPARSE_IRQ
arch/x86/include/asm/jump_label.h +0 -0
index a32b18c..872b3e1 100644
@@ -14,7 +14,7 @@
Quoted 8 lines

arch/x86/include/asm/jump_label.h line 17

Quoted 5 lines
arch/x86/include/asm/jump_label.h +1 -1
 static __always_inline bool arch_static_branch(struct jump_label_key *key)
 {
 	asm goto("1:"
-		JUMP_LABEL_INITIAL_NOP
+		"jmp %l[l_yes]\n"

Is it possible that the compiler choose a jump that is not 2 or 5-byte ? e.g. a jmp rel16 (e9 opcode) on 32-bit x86, or any of the other

Isn't e9 the 5 byte opcode?

Quoted 2 lines

instruction listed under the JMP-Jump instruction in the Intel insn manual ?

If it does, it will fail to compile. Look at the make_nop_x86(). It will die if it doesn't see either a eb or e9

Quoted 55 lines
Quoted 38 lines
arch/x86/include/asm/jump_label.h +0 -0
 		".pushsection __jump_table,  \"aw\" \n\t"
 		_ASM_ALIGN "\n\t"
 		_ASM_PTR "1b, %l[l_yes], %c0 \n\t"
arch/x86/kernel/jump_label.c +16 -1
index ea9d5f2f..d5b84de 100644
@@ -16,12 +16,27 @@
 
 #ifdef HAVE_JUMP_LABEL
 
+static unsigned char nop_short[] = { P6_NOP2 };
+
+/* These are the nops added at compile time */
+#ifdef CONFIG_X86_32
+static unsigned char default_nop[5] = { 0x3e, 0x8d, 0x74, 0x26, 0x00 };
+#else
+static unsigned char default_nop[5] = { 0x0f, 0x1f, 0x44, 0x00, 0x00 };
+#endif
+
+static int update_nops;
+
 union jump_code_union {
 	char code[JUMP_LABEL_NOP_SIZE];
 	struct {
 		char jump;
 		int offset;
-	} __attribute__((packed));
+	} __packed;
+	struct {
+		char jump_short;
+		char offset_short;
+	} __packed;
 };
 
 static void __jump_label_transform(struct jump_entry *entry,

arch/x86/kernel/jump_label.c static void __jump_label_transform(struct jump_entry *entry, · line 51

Quoted 8 lines
arch/x86/kernel/jump_label.c +5 -0
@@ -29,20 +44,70 @@ static void __jump_label_transform(struct jump_entry *entry,
 				   void *(*poker)(void *, const void *, size_t))
 {
 	union jump_code_union code;
+	unsigned char nop;
+	unsigned char op;
+	unsigned size;
+	void *ip = (void *)entry->code;
+	void *ideal = (void *)ideal_nops[NOP_ATOMIC5];

"void *" should possibly be "unsigned char *" here to respect the nop place-holder typing.

Quoted 5 lines
arch/x86/kernel/jump_label.c +4 -0
+
+	/* Use probe_kernel_read()? */
+	op = *(unsigned char *)ip;
+	nop = ideal_nops[NOP_ATOMIC5][0];
 
Quoted 12 lines

arch/x86/kernel/jump_label.c static void __jump_label_transform(struct jump_entry *entry, · line 59

Quoted 8 lines
arch/x86/kernel/jump_label.c +2 -5
 	if (type == JUMP_LABEL_ENABLE) {
-		code.jump = 0xe9;
-		code.offset = entry->target -
-				(entry->code + JUMP_LABEL_NOP_SIZE);
-	} else
-		memcpy(&code, ideal_nops[NOP_ATOMIC5], JUMP_LABEL_NOP_SIZE);
+		if (op == 0xe9 || op == 0xeb)
+			/* Already enabled. Warn? */

This could be caused by failure to run the link-time script, or running the transform twice. A warning would indeed be welcome, as this should never happen.

That's why I commented this :-)

-- Steve

#11 lore raw

Re: [PATCH 2/2] jump labels/x86: Use etiher 5 byte or 2 byte jumps

Quoted 6 lines · Steven Rostedt
Quoted 3 lines

Is it possible that the compiler choose a jump that is not 2 or 5-byte ? e.g. a jmp rel16 (e9 opcode) on 32-bit x86, or any of the other

Isn't e9 the 5 byte opcode?

I think Mathieu is thinking of 66 e9 rel16 (66 e9 xx xx) which isn't used by the compiler since it truncates the result to 16 bits.

-hpa