diff options
author | Remy Noel <mocramis@gmail.com> | 2018-04-14 10:00:10 +0200 |
---|---|---|
committer | Remy Noel <mocramis@gmail.com> | 2018-04-14 10:00:10 +0200 |
commit | f3bdc3f165ccb4bcd608f3f3725e990b44de44f9 (patch) | |
tree | 4714c20dd154fd1dd4a40faef2e454148c873136 | |
parent | 2efc39da83bf826186497a8fe3208fcc1afee2a4 (diff) | |
parent | 8088131a4e0165938593577a9cfa87de7ffd22bc (diff) |
Merge branch 'master' into perfmon
-rw-r--r-- | Makefile | 13 | ||||
-rw-r--r-- | arch/x86/Makefile | 9 | ||||
-rw-r--r-- | arch/x86/machine/lapic.c | 6 | ||||
-rw-r--r-- | arch/x86/x15.lds.S | 21 | ||||
-rw-r--r-- | doc/intro.9.txt | 2 | ||||
-rw-r--r-- | kern/Makefile | 1 | ||||
-rw-r--r-- | kern/bulletin.c | 83 | ||||
-rw-r--r-- | kern/bulletin.h | 75 | ||||
-rw-r--r-- | kern/bulletin_i.h | 35 | ||||
-rw-r--r-- | kern/hash.h | 23 | ||||
-rw-r--r-- | kern/list.h | 27 | ||||
-rw-r--r-- | kern/rcu.c | 2 | ||||
-rw-r--r-- | kern/xcall.c | 2 | ||||
-rw-r--r-- | test/Kconfig | 3 | ||||
-rw-r--r-- | test/Makefile | 1 | ||||
-rw-r--r-- | test/test_bulletin.c | 83 | ||||
-rwxr-xr-x | tools/build_configs.py | 1 | ||||
-rwxr-xr-x | tools/qemu.sh | 16 |
18 files changed, 361 insertions, 42 deletions
@@ -9,6 +9,8 @@ all: x15 docs VERSION = 0.1 export VERSION +COMMA := , + ifndef V V := 0 endif @@ -254,6 +256,10 @@ endif XBUILD_CFLAGS += -fsigned-char XBUILD_CFLAGS += -fno-common +# XXX Some assemblers consider the / symbol to denote comments. The --divide +# option suppresses that behavior. +XBUILD_CFLAGS += $(call xbuild_check_cc_option,-Wa$(COMMA)--divide) + XBUILD_CFLAGS += -Wall XBUILD_CFLAGS += -Wextra XBUILD_CFLAGS += -Wshadow @@ -266,7 +272,10 @@ XBUILD_CFLAGS += -Wno-unneeded-internal-declaration XBUILD_CFLAGS += $(call xbuild_check_cc_option,-fno-PIE) XBUILD_CFLAGS += $(call xbuild_check_cc_option,-Qunused-arguments) -XBUILD_LDFLAGS += -static -nostdlib -lgcc +XBUILD_LDFLAGS += -static -nostdlib + +# Disable the build ID feature of the linker +XBUILD_LDFLAGS += -Wl,--build-id=none x15_SOURCES-y := x15_LDS_S := arch/$(ARCH)/x15.lds.S @@ -294,7 +303,7 @@ x15_OBJECTS := $(call xbuild_replace_source_suffix,o,$(x15_SOURCES)) x15_LDS_D := $(call xbuild_gen_linker_script_depfile,$(x15_LDS_S)) x15_LDS := $(basename $(x15_LDS_S)) -XBUILD_LDFLAGS += -Xlinker -T $(x15_LDS) +XBUILD_LDFLAGS += -Wl,--script=$(x15_LDS) define gen_sorted_init_ops $(call xbuild_action,GEN,$@) \ diff --git a/arch/x86/Makefile b/arch/x86/Makefile index e20a711..002ed44 100644 --- a/arch/x86/Makefile +++ b/arch/x86/Makefile @@ -20,11 +20,20 @@ XBUILD_LDFLAGS += -Wl,-z,max-page-size=4096 -Wl,-z,common-page-size=4096 ifeq ($(CONFIG_X86_32),y) biarch := $(call cc-option,-m32) + XBUILD_CPPFLAGS += -m32 + + XBUILD_LDFLAGS += -lgcc else biarch := -m64 + XBUILD_CPPFLAGS += -m64 + # XXX The kernel isn't linked with libgcc on amd64, because libgcc + # uses red zones. But since there are actually very few functions + # provided by libgcc on amd64, it was decided to just not link with + # it. If it turns out that libgcc is really needed some day, the + # kernel can somewhat easily be changed to support red zones. XBUILD_CFLAGS += -mno-red-zone XBUILD_CFLAGS += -mcmodel=kernel endif diff --git a/arch/x86/machine/lapic.c b/arch/x86/machine/lapic.c index 180a1a2..3f6d0c2 100644 --- a/arch/x86/machine/lapic.c +++ b/arch/x86/machine/lapic.c @@ -271,11 +271,17 @@ lapic_ap_setup(void) static void lapic_ipi(uint32_t apic_id, uint32_t icr) { + unsigned long flags; + + cpu_intr_save(&flags); + if ((icr & LAPIC_ICR_DEST_MASK) == 0) { lapic_write(&lapic_map->icr_high, apic_id << LAPIC_DEST_SHIFT); } lapic_write(&lapic_map->icr_low, icr & ~LAPIC_ICR_RESERVED); + + cpu_intr_restore(flags); } static void diff --git a/arch/x86/x15.lds.S b/arch/x86/x15.lds.S index 80b1e65..b896a5d 100644 --- a/arch/x86/x15.lds.S +++ b/arch/x86/x15.lds.S @@ -58,7 +58,7 @@ SECTIONS _percpu = .; .percpu 0 : AT(BOOT_VTOP(_percpu)) { - *(.percpu) + *(.percpu*) } : percpu . = _percpu + SIZEOF(.percpu); @@ -67,18 +67,14 @@ SECTIONS _text = .; .text ALIGN(PAGE_SIZE) : AT(BOOT_VTOP(ADDR(.text))) { - *(.text) + *(.text*) } : text . = ALIGN(PAGE_SIZE); _rodata = .; .rodata ALIGN(PAGE_SIZE) : AT(BOOT_VTOP(ADDR(.rodata))) { - *(.rodata) - } : rodata - - .notes ALIGN(CPU_DATA_ALIGN) : AT(BOOT_VTOP(ADDR(.notes))) { - *(.note.*) + *(.rodata*) } : rodata . = ALIGN(PAGE_SIZE); @@ -88,21 +84,18 @@ SECTIONS . = ALIGN(CPU_L1_SIZE); *(.data.read_mostly) . = ALIGN(CPU_L1_SIZE); - *(.data) + *(.data*) } : data .bss ALIGN(CPU_DATA_ALIGN) : AT(BOOT_VTOP(ADDR(.bss))) { - *(.bss) + *(.bss*) } : data . = ALIGN(PAGE_SIZE); _end = .; - /* - * XXX A global offset section is created because of linking with libgcc. - * Is it safe to discard it ? - */ /DISCARD/ : { - *(.eh_frame) + *(.eh_frame*) + *(.note*) } } diff --git a/doc/intro.9.txt b/doc/intro.9.txt index 45c0a3f..d4532cb 100644 --- a/doc/intro.9.txt +++ b/doc/intro.9.txt @@ -135,6 +135,8 @@ other development tools : module:kern/bitmap:: Arbitrary-length bit array. +module:kern/bulletin:: + Publish-subscribe mechanism. module:kern/cbuf:: Circular byte buffer. module:kern/clock:: diff --git a/kern/Makefile b/kern/Makefile index 3779380..5b04fcb 100644 --- a/kern/Makefile +++ b/kern/Makefile @@ -1,6 +1,7 @@ x15_SOURCES-y += \ kern/arg.c \ kern/bitmap.c \ + kern/bulletin.c \ kern/cbuf.c \ kern/clock.c \ kern/condition.c \ diff --git a/kern/bulletin.c b/kern/bulletin.c new file mode 100644 index 0000000..bbb1bfc --- /dev/null +++ b/kern/bulletin.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2017-2018 Richard Braun. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <stdint.h> + +#include <kern/bulletin.h> +#include <kern/list.h> +#include <kern/rcu.h> +#include <kern/spinlock.h> +#include <kern/thread.h> + +static void +bulletin_sub_init(struct bulletin_sub *sub, + bulletin_notif_fn_t notif_fn, void *arg) +{ + sub->notif_fn = notif_fn; + sub->arg = arg; +} + +static void +bulletin_sub_notify(const struct bulletin_sub *sub, uintptr_t value) +{ + sub->notif_fn(value, sub->arg); +} + +void +bulletin_init(struct bulletin *bulletin) +{ + spinlock_init(&bulletin->lock); + list_init(&bulletin->subs); +} + +void +bulletin_subscribe(struct bulletin *bulletin, struct bulletin_sub *sub, + bulletin_notif_fn_t notif_fn, void *arg) +{ + bulletin_sub_init(sub, notif_fn, arg); + + spinlock_lock(&bulletin->lock); + list_rcu_insert_tail(&bulletin->subs, &sub->node); + spinlock_unlock(&bulletin->lock); +} + +void +bulletin_unsubscribe(struct bulletin *bulletin, struct bulletin_sub *sub) +{ + spinlock_lock(&bulletin->lock); + list_rcu_remove(&sub->node); + spinlock_unlock(&bulletin->lock); + + rcu_wait(); +} + +void +bulletin_publish(struct bulletin *bulletin, uintptr_t value) +{ + struct bulletin_sub *sub; + + assert(!thread_interrupted()); + + rcu_read_enter(); + + list_rcu_for_each_entry(&bulletin->subs, sub, node) { + bulletin_sub_notify(sub, value); + } + + rcu_read_leave(); +} diff --git a/kern/bulletin.h b/kern/bulletin.h new file mode 100644 index 0000000..8ce9d9e --- /dev/null +++ b/kern/bulletin.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2017-2018 Richard Braun. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * + * Minimalist publish-subscribe mechanism. + */ + +#ifndef KERN_BULLETIN_H +#define KERN_BULLETIN_H + +#include <stdint.h> + +#include <kern/macros.h> +#include <kern/work.h> + +/* + * Type for bulletin notification functions. + * + * The value is passed from the publisher unmodified, and can safely be + * cast into a pointer. Notification functions run in the context of the + * publisher. + */ +typedef void (*bulletin_notif_fn_t)(uintptr_t value, void *arg); + +#include <kern/bulletin_i.h> + +struct bulletin; + +/* + * Bulletin subscriber. + */ +struct bulletin_sub; + +void bulletin_init(struct bulletin *bulletin); + +/* + * Subscribe to a bulletin. + * + * Once subscribed, the notification function is called with its argument + * each time the bulletin is published. + */ +void bulletin_subscribe(struct bulletin *bulletin, struct bulletin_sub *sub, + bulletin_notif_fn_t notif_fn, void *arg); + +/* + * Unsubscribe from a bulletin. + * + * On return, the subscriber notification function may not be called any more. + * + * This function synchronizes with RCU. + */ +void bulletin_unsubscribe(struct bulletin *bulletin, struct bulletin_sub *sub); + +/* + * Publish a bulletin. + * + * All subscribers are notified by calling their notification function, with + * the given value passed unmodified. + */ +void bulletin_publish(struct bulletin *bulletin, uintptr_t value); + +#endif /* KERN_BULLETIN_H */ diff --git a/kern/bulletin_i.h b/kern/bulletin_i.h new file mode 100644 index 0000000..ea0940a --- /dev/null +++ b/kern/bulletin_i.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017-2018 Richard Braun. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef KERN_BULLETIN_I_H +#define KERN_BULLETIN_I_H + +#include <kern/list.h> +#include <kern/spinlock.h> + +struct bulletin_sub { + struct list node; + bulletin_notif_fn_t notif_fn; + void *arg; +}; + +struct bulletin { + struct spinlock lock; + struct list subs; +}; + +#endif /* KERN_BULLETIN_I_H */ diff --git a/kern/hash.h b/kern/hash.h index 9d7778e..19803eb 100644 --- a/kern/hash.h +++ b/kern/hash.h @@ -43,6 +43,7 @@ #define KERN_HASH_H #include <assert.h> +#include <stdbool.h> #include <stdint.h> #ifdef __LP64__ @@ -54,11 +55,19 @@ static_assert(sizeof(long) == 4, "unsupported data model"); #define hash_long(n, bits) hash_int32(n, bits) #endif +static inline bool +hash_bits_valid(unsigned int bits) +{ + return (bits != 0) && (bits <= HASH_ALLBITS); +} + static inline uint32_t hash_int32(uint32_t n, unsigned int bits) { uint32_t hash; + assert(hash_bits_valid(bits)); + hash = n; hash = ~hash + (hash << 15); hash ^= (hash >> 12); @@ -75,6 +84,8 @@ hash_int64(uint64_t n, unsigned int bits) { uint64_t hash; + assert(hash_bits_valid(bits)); + hash = n; hash = ~hash + (hash << 21); hash ^= (hash >> 24); @@ -100,9 +111,11 @@ hash_ptr(const void *ptr, unsigned int bits) static inline unsigned long hash_str(const char *str, unsigned int bits) { - unsigned long hash; + unsigned long hash, mask; char c; + assert(hash_bits_valid(bits)); + for (hash = 0; /* no condition */; str++) { c = *str; @@ -113,7 +126,13 @@ hash_str(const char *str, unsigned int bits) hash = ((hash << 5) - hash) + c; } - return hash & ((1UL << bits) - 1); + /* + * This mask construction avoids the undefined behavior that would + * result from directly shifting by the number of bits, if that number + * is equal to the width of the hash. + */ + mask = (~0UL >> (HASH_ALLBITS - bits)); + return hash & mask; } #endif /* KERN_HASH_H */ diff --git a/kern/list.h b/kern/list.h index 832d94f..8163176 100644 --- a/kern/list.h +++ b/kern/list.h @@ -478,12 +478,12 @@ list_rcu_remove(struct list *node) * the node pointer can only be read once, preventing the combination * of lockless list_empty()/list_first_entry() variants. */ -#define list_rcu_first_entry(list, type, member) \ +#define list_rcu_first_entry(head, type, member) \ MACRO_BEGIN \ struct list *list___; \ struct list *first___; \ \ - list___ = (list); \ + list___ = (head); \ first___ = list_rcu_first(list___); \ list_end(list___, first___) \ ? NULL \ @@ -497,8 +497,17 @@ MACRO_END * the node pointer can only be read once, preventing the combination * of lockless list_empty()/list_next_entry() variants. */ -#define list_rcu_next_entry(entry, member) \ - list_rcu_first_entry(&entry->member, typeof(*entry), member) +#define list_rcu_next_entry(head, entry, member) \ +MACRO_BEGIN \ + struct list *list___; \ + struct list *next___; \ + \ + list___ = (head); \ + next___ = list_rcu_next(&entry->member); \ + list_end(list___, next___) \ + ? NULL \ + : list_entry(next___, typeof(*entry), member); \ +MACRO_END /* * Forge a loop to process all nodes of a list. @@ -515,11 +524,9 @@ for (node = list_rcu_first(list); \ * * The entry node must not be altered during the loop. */ -#define list_rcu_for_each_entry(list, entry, member) \ -for (entry = list_rcu_entry(list_first(list), \ - typeof(*entry), member); \ - !list_end(list, &entry->member); \ - entry = list_rcu_entry(list_next(&entry->member), \ - typeof(*entry), member)) +#define list_rcu_for_each_entry(list, entry, member) \ +for (entry = list_rcu_first_entry(list, typeof(*entry), member); \ + entry != NULL; \ + entry = list_rcu_next_entry(list, entry, member)) #endif /* KERN_LIST_H */ @@ -706,6 +706,8 @@ rcu_defer(struct work *work) struct rcu_cpu_data *cpu_data; unsigned long flags; + assert(!rcu_reader_in_cs(thread_rcu_reader(thread_self()))); + thread_preempt_disable_intr_save(&flags); cpu_data = rcu_get_cpu_data(); rcu_cpu_data_queue(cpu_data, work); diff --git a/kern/xcall.c b/kern/xcall.c index 2096f61..74a5bcb 100644 --- a/kern/xcall.c +++ b/kern/xcall.c @@ -177,7 +177,7 @@ xcall_intr(void) if (call) { xcall_process(call); } else { - log_warning("xcall: spurious interrupt on cpu%u", cpu_id()); + log_err("xcall: spurious interrupt on cpu%u", cpu_id()); } syscnt_inc(&cpu_data->sc_received); diff --git a/test/Kconfig b/test/Kconfig index 1355c1a..df2272e 100644 --- a/test/Kconfig +++ b/test/Kconfig @@ -9,6 +9,9 @@ if TEST_MODULE choice prompt "Select test module" +config TEST_MODULE_BULLETIN + bool "bulletin" + config TEST_MODULE_MUTEX bool "mutex" select MUTEX_DEBUG diff --git a/test/Makefile b/test/Makefile index e6526be..b6f2260 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,3 +1,4 @@ +x15_SOURCES-$(CONFIG_TEST_MODULE_BULLETIN) += test/test_bulletin.c x15_SOURCES-$(CONFIG_TEST_MODULE_MUTEX) += test/test_mutex.c x15_SOURCES-$(CONFIG_TEST_MODULE_MUTEX_PI) += test/test_mutex_pi.c x15_SOURCES-$(CONFIG_TEST_MODULE_PERFMON_CPU) += test/test_perfmon_cpu.c diff --git a/test/test_bulletin.c b/test/test_bulletin.c new file mode 100644 index 0000000..4a2fdb5 --- /dev/null +++ b/test/test_bulletin.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2018 Richard Braun. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * + * This test makes a bulletin subscriber subscribe to a bulletin, and uses + * a timer to periodically publish, unsubscribe, and resubscribe to the + * bulletin. A counter is used to test passing values to the notification + * callback function. + */ + +#include <assert.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> + +#include <kern/bulletin.h> +#include <kern/clock.h> +#include <kern/error.h> +#include <kern/log.h> +#include <kern/timer.h> +#include <test/test.h> + +#define TEST_INTERVAL 1 +#define TEST_NR_LOOPS 4 + +static struct bulletin test_bulletin; +static struct bulletin_sub test_bulletin_sub; +static struct timer test_timer; +static unsigned int test_counter; + +static void +test_notify(uintptr_t value, void *arg) +{ + log_info("test: notify: value:%lu arg:%p", value, arg); +} + +static void +test_tick(struct timer *timer) +{ + uint64_t ticks; + + test_counter++; + bulletin_publish(&test_bulletin, test_counter); + bulletin_unsubscribe(&test_bulletin, &test_bulletin_sub); + + if (test_counter == TEST_NR_LOOPS) { + log_info("test: done"); + return; + } + + bulletin_subscribe(&test_bulletin, &test_bulletin_sub, + test_notify, (void *)0x123); + + ticks = timer_get_time(timer) + clock_ticks_from_ms(TEST_INTERVAL * 1000); + timer_schedule(&test_timer, ticks); +} + +void __init +test_setup(void) +{ + uint64_t ticks; + + bulletin_init(&test_bulletin); + bulletin_subscribe(&test_bulletin, &test_bulletin_sub, + test_notify, (void *)0x123); + + timer_init(&test_timer, test_tick, TIMER_DETACHED); + ticks = clock_get_time() + clock_ticks_from_ms(TEST_INTERVAL * 1000); + timer_schedule(&test_timer, ticks); +} diff --git a/tools/build_configs.py b/tools/build_configs.py index 95a5905..dab95b7 100755 --- a/tools/build_configs.py +++ b/tools/build_configs.py @@ -109,6 +109,7 @@ large_options_dict.update({ # TODO Generate this list from test/test_*.c test_list = [ + 'CONFIG_TEST_MODULE_BULLETIN', 'CONFIG_TEST_MODULE_MUTEX', 'CONFIG_TEST_MODULE_MUTEX_PI', 'CONFIG_TEST_MODULE_PMAP_UPDATE_MP', diff --git a/tools/qemu.sh b/tools/qemu.sh index 1e1990b..3aef63e 100755 --- a/tools/qemu.sh +++ b/tools/qemu.sh @@ -22,18 +22,8 @@ KVM="-enable-kvm -cpu host" X15=$PWD/x15 TMPDIR=$(mktemp -d) -CDROOT=$TMPDIR/cdroot -mkdir -p $CDROOT/boot/grub -cp $X15 $CDROOT/boot -cat > $CDROOT/boot/grub/grub.cfg << EOF -set timeout=1 - -menuentry "X15" --class os { - multiboot (hd96)/boot/x15 root=device:hd1s8 -} -EOF -grub-mkrescue -o $TMPDIR/grub.iso $CDROOT +objcopy -O elf32-i386 $X15 $TMPDIR/x15 $QEMU_EXE $KVM \ -ctrl-grab \ @@ -41,7 +31,7 @@ $QEMU_EXE $KVM \ -m $RAM \ -smp $NR_CPUS \ -monitor stdio \ - -cdrom $TMPDIR/grub.iso \ - -boot d + -kernel $TMPDIR/x15 \ + -append "console=atcons" rm -rf $TMPDIR |