diff --git a/arch/sparc/include/asm/switch_to.h b/arch/sparc/include/asm/switch_to.h index 8058fac6e4e377d719ef3f716c1ec845e2723fca..cb6185c8f5f94e448a8e1d8fecc9a0423c9426eb 100644 --- a/arch/sparc/include/asm/switch_to.h +++ b/arch/sparc/include/asm/switch_to.h @@ -187,7 +187,7 @@ __asm__ __volatile__( \ " nop\n\t" \ "here:\n\t" \ : \ - : "r" (&(current_set[0])), \ + : "r" (&(current_set[leon3_cpuid()])), \ "r" (&(next->thread_info)), \ "i" (TI_KSP), \ "i" (TI_KPC), \ diff --git a/arch/sparc/kernel/bootmem.c b/arch/sparc/kernel/bootmem.c index c5f20aca66cbeaad0769da82d042c93963daf145..867373144b05d18185186e1269fd7ddb4ab6ac88 100644 --- a/arch/sparc/kernel/bootmem.c +++ b/arch/sparc/kernel/bootmem.c @@ -237,7 +237,7 @@ void bootmem_init(void) BUG_ON(mem_size > (1UL << MM_BLOCK_ORDER_MAX)); BUG_ON(PAGE_SIZE < (1UL << MM_BLOCK_ORDER_MIN)); - BUG_ON(page_map_init(mm_init_page_map, base_pfn, end_pfn, PAGE_SIZE)); + BUG_ON(page_map_init(mm_init_page_map, start_pfn, end_pfn, PAGE_SIZE)); /* reserve all space up to the end of the image, so mapping starts diff --git a/arch/sparc/kernel/head.S b/arch/sparc/kernel/head.S index 768586a28c05e16be8a555d2805727d1fb762533..4033c51f1bd6dcf914d08e30dbef642929110d50 100644 --- a/arch/sparc/kernel/head.S +++ b/arch/sparc/kernel/head.S @@ -40,6 +40,13 @@ kernel_entry: * inserting (3) nops. */ + /* get our cpu id */ + rd %asr17, %g3 + srl %g3, 28, %g3 + + cmp %g3, %g0 + bne setup_wim + nop /* Clear the .bss section or we'll be in trouble later. This section * is supposed to be double-word aligned (see kernel.lds.S), so we @@ -63,7 +70,7 @@ zero_bss: /* Now we initialise the window invalid mask to detect under/overflows. * We set the highest-value window to be invalid. */ - +setup_wim: mov (1 << (NWINDOWS - 1)), %g1 mov %g1, %wim ! delayed-write instruction @@ -102,8 +109,17 @@ zero_bss: nop nop + cmp %g3, %g0 + bne slavego + nop + + call do_basic_setup nop call kernel_main nop + +slavego: + call smp_cpu_entry + nop diff --git a/arch/sparc/kernel/irq.c b/arch/sparc/kernel/irq.c index b5e4ea9a45cea04edc26541af2d2a474459edbe1..8a1173585a5dec524eb67b3bb276a60676be965b 100644 --- a/arch/sparc/kernel/irq.c +++ b/arch/sparc/kernel/irq.c @@ -12,11 +12,11 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * @todo eventually replace catch_interrupt() libgloss/newlib functionality - * with local/custom code and rework the globals * @todo irq configuration should only be done through a syscall, * so traps are disabled * + * @todo this needs locking for all resouces shared between CPUs + * * @brief an IRQ manager that dispatches interrupts to registered * handler functions * @@ -74,7 +74,6 @@ struct irl_vector_elem { #define IRL_QUEUE_SIZE 64 - #ifdef CONFIG_LEON2 #define IRL_SIZE LEON2_IRL_SIZE #define EIRL_SIZE LEON2_EIRL_SIZE @@ -93,6 +92,9 @@ static struct leon3_irqctrl_registermap *leon_irqctrl_regs; #endif /* CONFIG_LEON3 */ +#define CPU_AFFINITY_NONE (-1) + +static int irq_cpu_affinity[IRL_SIZE + EIRL_SIZE]; static struct list_head irl_pool_head; static struct list_head irl_queue_head; @@ -394,7 +396,7 @@ static void leon_clear_irq(unsigned int irq) * @param cpu the cpu for which the interrupt is to be unmasked */ -static void leon_unmask_irq(unsigned int irq, unsigned int cpu) +static void leon_unmask_irq(unsigned int irq, int cpu) { uint32_t mask; @@ -426,7 +428,7 @@ static void leon_unmask_irq(unsigned int irq, unsigned int cpu) * @param cpu the cpu for which the interrupt is to be masked */ -static void leon_mask_irq(unsigned int irq, unsigned int cpu) +static void leon_mask_irq(unsigned int irq, int cpu) { uint32_t mask; @@ -459,7 +461,7 @@ static void leon_mask_irq(unsigned int irq, unsigned int cpu) * @param cpu the cpu for which the interrupt is to be enabled */ -static void leon_enable_irq(unsigned int irq, unsigned int cpu) +static void leon_enable_irq(unsigned int irq, int cpu) { leon_clear_irq(irq); @@ -474,7 +476,7 @@ static void leon_enable_irq(unsigned int irq, unsigned int cpu) * @param cpu the cpu for which the interrupt is to be disabled */ -static void leon_disable_irq(unsigned int irq, unsigned int cpu) +static void leon_disable_irq(unsigned int irq, int cpu) { leon_clear_irq(irq); @@ -598,7 +600,7 @@ static int leon_eirq_dispatch(unsigned int irq) { unsigned int eirq; #ifdef CONFIG_LEON3 - unsigned int cpu; + int cpu; #endif /* CONFIG_LEON3 */ struct irl_vector_elem *p_elem; @@ -682,6 +684,8 @@ int irl_register_handler(unsigned int irq, irq_handler_t handler, void *data) { + int cpu; + uint32_t psr_flags; struct irl_vector_elem *p_elem; @@ -711,8 +715,16 @@ int irl_register_handler(unsigned int irq, spin_lock_restore_irq(psr_flags); #ifdef CONFIG_LEON3 - /* XXX for now, just register to the current CPU */ - leon_enable_irq(irq, leon3_cpuid()); + /* XXX for now, just register to the current CPU if no affinity is set */ + cpu = irq_cpu_affinity[irq]; + + if (cpu == CPU_AFFINITY_NONE) { + cpu = leon3_cpuid(); + irq_cpu_affinity[irq] = cpu; + } + + leon_enable_irq(irq, cpu); + #endif /* CONFIG_LEON3 */ #ifdef CONFIG_LEON2 leon_enable_irq(irq, 0); @@ -749,6 +761,8 @@ static int eirl_register_handler(unsigned int irq, irq_handler_t handler, void *data) { + int cpu; + uint32_t psr_flags; struct irl_vector_elem *p_elem; @@ -781,8 +795,15 @@ static int eirl_register_handler(unsigned int irq, spin_lock_restore_irq(psr_flags); #ifdef CONFIG_LEON3 - /* XXX for now, just register to the current CPU */ - leon_enable_irq(irq, leon3_cpuid()); + /* XXX for now, just register to the current CPU if no affinity is set */ + cpu = irq_cpu_affinity[irq]; + + if (cpu == CPU_AFFINITY_NONE) { + cpu = leon3_cpuid(); + irq_cpu_affinity[irq] = cpu; + } + + leon_enable_irq(irq, cpu); #endif /* CONFIG_LEON3 */ #ifdef CONFIG_LEON2 leon_enable_irq(irq, 0); @@ -954,7 +975,8 @@ static int irq_dispatch_init(void) list_add_tail(&irl_queue_pool[i].handler_node, &irq_queue_pool_head); - + for (i = 0; i < IRL_SIZE + EIRL_SIZE; i++) + irq_cpu_affinity[i] = CPU_AFFINITY_NONE; return 0; } @@ -1026,8 +1048,9 @@ static void leon_setup_eirq(void) #endif /* CONFIG_ARCH_CUSTOM_BOOT_CODE */ #ifdef CONFIG_LEON3 - /* XXX for now, just register to the current CPU */ - leon_enable_irq(leon_eirq, leon3_cpuid()); + /* XXX enable for all cpus in the system */ + leon_enable_irq(leon_eirq, 0); + leon_enable_irq(leon_eirq, 1); #endif /* CONFIG_LEON3 */ #ifdef CONFIG_LEON2 leon_enable_irq(leon_eirq, 0); @@ -1134,6 +1157,24 @@ static void execute_deferred_irq(void) leon_irq_queue_execute(); } +/** + * @brief set IRQ affinity to a certain CPU + */ + +static void set_affinity(unsigned int irq, int cpu) +{ + /* XXX need CPU range check */ + + if (irq_cpu_affinity[irq] == cpu) + return; + + if (irq_cpu_affinity[irq] != CPU_AFFINITY_NONE) + leon_mask_irq(irq, irq_cpu_affinity[irq]); + + leon_enable_irq(irq, cpu); + + irq_cpu_affinity[irq] = cpu; +} /** @@ -1141,11 +1182,12 @@ static void execute_deferred_irq(void) */ static struct irq_dev leon_irq = { - .irq_enable = enable_irq, - .irq_disable = disable_irq, - .irq_mask = mask_irq, - .irq_unmask = unmask_irq, - .irq_deferred = execute_deferred_irq, + .irq_enable = enable_irq, + .irq_disable = disable_irq, + .irq_mask = mask_irq, + .irq_unmask = unmask_irq, + .irq_deferred = execute_deferred_irq, + .irq_set_affinity = set_affinity, }; @@ -1162,6 +1204,10 @@ void leon_irq_init(void) /* mask all interrupts on this (boot) CPU */ iowrite32be(0, &leon_irqctrl_regs->irq_mpmask[leon3_cpuid()]); + + /* XXX MASK FOR ALL CPUS CONFIGURED FOR THE SYSTEM (dummy for N==2)*/ + iowrite32be(0, &leon_irqctrl_regs->irq_mpmask[1]); + #endif /* CONFIG_LEON3 */ #ifdef CONFIG_LEON2 leon_irqctrl_regs = (struct leon2_irqctrl_registermap *) diff --git a/arch/sparc/kernel/setup.c b/arch/sparc/kernel/setup.c index 115a9b595d3a082bef6f0c0ec70d642916744217..dc5096d8b396c789888cdc966bb9627529165192 100644 --- a/arch/sparc/kernel/setup.c +++ b/arch/sparc/kernel/setup.c @@ -14,6 +14,7 @@ #include <asm/irq.h> #include <asm/time.h> #include <asm/clockevent.h> +#include <kernel/clockevent.h> #include <compiler.h> #include <page.h> @@ -55,6 +56,8 @@ static void reserve_kernel_stack(void) /* the (aligned) top of the stack */ _kernel_stack_top = (void *) (char *) _kernel_stack_bottom + k_stack_sz; _kernel_stack_top = ALIGN_PTR(_kernel_stack_top, STACK_ALIGN); + + printk("xxxxx reserved %p to %p\n", _kernel_stack_top, _kernel_stack_bottom); } @@ -86,6 +89,59 @@ static void mem_init(void) } +int cpu1_ready; + + +#include <asm/io.h> +/* wake a cpu by writing to the multiprocessor status register */ +void cpu_wake(uint32_t cpu_id) +{ + iowrite32be(cpu_id, (uint32_t *) 0x80000210); +} + +/** XXX crappy */ +static void boot_cpus(void) +{ + printk("booting cpu1\n"); + cpu_wake(0x2); /*cpu 1 */ + + while (!ioread32be(&cpu1_ready)); + printk("cpu1 booted\n"); +} + + +#include <asm/processor.h> +#include <kernel/kthread.h> +extern struct task_struct *kernel[]; +void smp_cpu_entry(void) +{ + + reserve_kernel_stack(); + BUG_ON(stack_migrate(NULL, _kernel_stack_top)); + + printk("hi i'm cpu %d\n", leon3_cpuid()); + + BUG_ON(!leon3_cpuid()); + /* signal ready */ + iowrite32be(0x1, &cpu1_ready); + + while (ioread32be(&cpu1_ready) != 0x2); + BUG_ON(clockevents_offer_device()); + kthread_init_main(); + + iowrite32be(0x3, &cpu1_ready); + while (ioread32be(&cpu1_ready) != 0x4); + while(1) { + // printk("x"); +// cpu_relax(); + } +// printk("1\n"); +} + + + + + /** * @brief architecture setup entry point */ @@ -107,4 +163,6 @@ void setup_arch(void) sparc_uptime_init(); sparc_clockevent_init(); + + boot_cpus(); } diff --git a/arch/sparc/kernel/thread.c b/arch/sparc/kernel/thread.c index 28ce210dc37f34c773bdcab59d07396b127fd8e5..73cef273e6b082f64d6f883061db092382033e43 100644 --- a/arch/sparc/kernel/thread.c +++ b/arch/sparc/kernel/thread.c @@ -40,7 +40,7 @@ extern struct thread_info *current_set[]; #include <kernel/time.h> static void th_starter(void) { - struct task_struct *task = current_set[0]->task; + struct task_struct *task = current_set[leon3_cpuid()]->task; struct timespec ts; double start; @@ -112,7 +112,7 @@ void arch_promote_to_task(struct task_struct *task) task->data = NULL; - printk(MSG "kernel stack %x\n", leon_get_fp()); + printk(MSG "kernel stack %x %x\n", leon_get_fp(), leon_get_sp()); printk(MSG "is next at %p stack %p\n", &task->thread_info, task->stack); diff --git a/include/kernel/bitops.h b/include/kernel/bitops.h index 404953e52960269a95ece9d71f4f13e4dabc6775..401a06bb2e0016434470df62bf43e0812e47d473 100644 --- a/include/kernel/bitops.h +++ b/include/kernel/bitops.h @@ -17,6 +17,11 @@ #define BITS_PER_LONG __WORDSIZE #endif +#ifndef BITS_PER_LONG_LONG +#define BITS_PER_LONG_LONG (__WORDSIZE * 2) +#endif + + #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) diff --git a/include/kernel/clockevent.h b/include/kernel/clockevent.h index 1e1599544ec82d18c907f3e3876d0abf03a49a01..0a36a4e84e74381314ccf8cabaa1b9bdb8aed9eb 100644 --- a/include/kernel/clockevent.h +++ b/include/kernel/clockevent.h @@ -100,6 +100,8 @@ void clockevents_set_handler(struct clock_event_device *dev, void clockevents_register_device(struct clock_event_device *dev); +int clockevents_offer_device(void); + void clockevents_exchange_device(struct clock_event_device *old, struct clock_event_device *new); @@ -110,4 +112,5 @@ int clockevents_program_timeout_ns(struct clock_event_device *dev, unsigned long nanoseconds); + #endif /* _KERNEL_CLOCKEVENT_H_ */ diff --git a/include/kernel/irq.h b/include/kernel/irq.h index b08bd0470c4dc713286f5e71c9c9f5982b6711e3..df691b22b1359b3b8c6ca183c1b81559377e33f8 100644 --- a/include/kernel/irq.h +++ b/include/kernel/irq.h @@ -42,11 +42,12 @@ struct irq_data { }; struct irq_dev { - unsigned int (*irq_enable) (struct irq_data *data); - void (*irq_disable) (struct irq_data *data); - void (*irq_mask) (struct irq_data *data); - void (*irq_unmask) (struct irq_data *data); - void (*irq_deferred) (void); + unsigned int (*irq_enable) (struct irq_data *data); + void (*irq_disable) (struct irq_data *data); + void (*irq_mask) (struct irq_data *data); + void (*irq_unmask) (struct irq_data *data); + void (*irq_deferred) (void); + void (*irq_set_affinity) (unsigned int irq, int cpu); }; @@ -60,4 +61,6 @@ int irq_request(unsigned int irq, enum isr_exec_priority priority, int irq_exec_deferred(void); +int irq_set_affinity(unsigned int irq, int cpu); + #endif /* _KERNEL_IRQ_H_ */ diff --git a/include/kernel/kthread.h b/include/kernel/kthread.h index 33d03f00e3bfe73b8d599fe3c55b0864949e1173..a72c686f285f2e17ee18971bbe5c83fed4d47473 100644 --- a/include/kernel/kthread.h +++ b/include/kernel/kthread.h @@ -34,6 +34,7 @@ struct remove_this_declaration { #define TASK_IDLE 0x0001 #define TASK_NEW 0x0002 #define TASK_DEAD 0x0004 +#define TASK_BUSY 0x0005 diff --git a/include/kernel/sched.h b/include/kernel/sched.h index fbb3ba55289737dd97c62173f6840d4ca373e030..c080b627f141d2cfb99d2b34422f1e35f99a8bc3 100644 --- a/include/kernel/sched.h +++ b/include/kernel/sched.h @@ -64,14 +64,14 @@ struct scheduler { const enum sched_policy policy; - struct task_struct *(*pick_next_task)(struct task_queue *tq); + struct task_struct *(*pick_next_task)(struct task_queue *tq, ktime now); /* XXX: sucks */ - void (*wake_next_task)(struct task_queue *tq); + void (*wake_next_task)(struct task_queue *tq, ktime now); void (*enqueue_task) (struct task_queue *tq, struct task_struct *task); ktime (*timeslice_ns) (struct task_struct *task); - ktime (*task_ready_ns) (struct task_queue *tq); + ktime (*task_ready_ns) (struct task_queue *tq, ktime now); int (*check_sched_attr) (struct sched_attr *attr); diff --git a/include/kernel/string.h b/include/kernel/string.h index b5a18adfac2e3f9229d98f5338bc3cb79f2068e6..c0bd31db6a4192a3ef23f0fbf1ab05e3730ee260 100644 --- a/include/kernel/string.h +++ b/include/kernel/string.h @@ -37,7 +37,7 @@ int islower(int c); int atoi(const char *nptr); long int strtol(const char *nptr, char **endptr, int base); - +long long int strtoll(const char *nptr, char **endptr, int base); int vprintf(const char *format, va_list ap); int vsprintf(char *str, const char *format, va_list ap); diff --git a/init/main.c b/init/main.c index 27bb193113c0defcca66edcd3ec17e9ccab78b68..05778b6eedfd37b8a573dfd328178c806f4718b1 100644 --- a/init/main.c +++ b/init/main.c @@ -52,8 +52,8 @@ int task1(void *data) xa++; - //printk("#"); -// sched_yield(); + // printk("# %d #\n", leon3_cpuid()); + // sched_yield(); } } @@ -65,7 +65,7 @@ int task2(void *data) //printk("_"); xb++; // sched_yield(); -// printk("-"); + // printk("-"); // sched_yield(); } } @@ -89,28 +89,52 @@ int task3(void *data) #endif //printk("y %llu\n", ktime_get()); - //printk("."); + // printk("."); xc++; // sched_yield(); } } - - +#include <kernel/sysctl.h> +extern ktime sched_last_time; +extern uint32_t sched_ev; int task0(void *data) { + int last = 0; + int curr = 0; + char buf1[64]; + + uint32_t last_call = 0; + + ktime sched_time; + struct sysobj *sys_irq = NULL; + + + + printk("HELLO! %d\n", leon3_cpuid()); + bzero(buf1, 64); + sys_irq = sysset_find_obj(sys_set, "/sys/irl/primary"); + + int a, b, c; while (1) { + + + if (sys_irq) + sysobj_show_attr(sys_irq, "irl", buf1); + a = xa; b = xb; c = xc; - printk("%d %d %d %llu\n", a, b, c, ktime_get()); - sched_yield(); + sched_time = sched_last_time; + curr = atoi(buf1)/2; + printk("%d %d %d %llu irq: %s %d per sec; sched %llu us %llu per call, calls %d cpu %d\n", a, b, c, ktime_get(), buf1, (curr -last), ktime_to_us(sched_last_time), sched_last_time /sched_ev, sched_ev - last_call, leon3_cpuid()); + last = curr; + last_call = sched_ev; +// sched_yield(); } } -extern struct task_struct *kernel; - /** * @brief kernel initialisation routines @@ -129,6 +153,8 @@ arch_initcall(kernel_init); +/** XXX dummy **/ +extern int cpu1_ready; /** * @brief kernel main functionputchar( *((char *) data) ); */ @@ -193,7 +219,7 @@ int kernel_main(void) /* elevate boot thread */ - kernel = kthread_init_main(); + kthread_init_main(); #if 0 /* * T1: (P=50, D=20, R=10) @@ -225,7 +251,7 @@ int kernel_main(void) - + cpu1_ready = 2; #if 1 { struct sched_attr attr; @@ -243,21 +269,22 @@ int kernel_main(void) kthread_wake_up(t); #endif #if 1 +#if 0 t = kthread_create(task2, NULL, KTHREAD_CPU_AFFINITY_NONE, "print1"); sched_get_attr(t, &attr); attr.policy = SCHED_EDF; - attr.period = us_to_ktime(1000); - attr.deadline_rel = us_to_ktime(900); - attr.wcet = us_to_ktime(200); + attr.period = ms_to_ktime(1000); + attr.deadline_rel = ms_to_ktime(900); + attr.wcet = ms_to_ktime(200); sched_set_attr(t, &attr); kthread_wake_up(t); #if 1 t = kthread_create(task3, NULL, KTHREAD_CPU_AFFINITY_NONE, "print2"); sched_get_attr(t, &attr); attr.policy = SCHED_EDF; - attr.period = us_to_ktime(800); - attr.deadline_rel = us_to_ktime(700); - attr.wcet = us_to_ktime(100); + attr.period = ms_to_ktime(800); + attr.deadline_rel = ms_to_ktime(700); + attr.wcet = ms_to_ktime(200); sched_set_attr(t, &attr); kthread_wake_up(t); #endif @@ -265,19 +292,32 @@ int kernel_main(void) t = kthread_create(task1, NULL, KTHREAD_CPU_AFFINITY_NONE, "print3"); sched_get_attr(t, &attr); attr.policy = SCHED_EDF; - attr.period = us_to_ktime(400); - attr.deadline_rel = us_to_ktime(200); - attr.wcet = us_to_ktime(100); + attr.period = ms_to_ktime(400); + attr.deadline_rel = ms_to_ktime(200); + attr.wcet = ms_to_ktime(100); sched_set_attr(t, &attr); kthread_wake_up(t); #endif + +#endif #if 1 - t = kthread_create(task0, NULL, KTHREAD_CPU_AFFINITY_NONE, "res"); + + t = kthread_create(task0, NULL, 0, "res"); sched_get_attr(t, &attr); attr.policy = SCHED_EDF; - attr.period = ms_to_ktime(2000); + attr.period = ms_to_ktime(1000); attr.deadline_rel = ms_to_ktime(900); - attr.wcet = ms_to_ktime(100); + attr.wcet = ms_to_ktime(800); + sched_set_attr(t, &attr); + kthread_wake_up(t); +#endif +#if 1 + t = kthread_create(task1, NULL, 1, "print3"); + sched_get_attr(t, &attr); + attr.policy = SCHED_EDF; + attr.period = ms_to_ktime(500); + attr.deadline_rel = ms_to_ktime(300); + attr.wcet = ms_to_ktime(200); sched_set_attr(t, &attr); kthread_wake_up(t); #endif @@ -289,6 +329,9 @@ int kernel_main(void) } #endif + + while (ioread32be(&cpu1_ready) != 0x3); + iowrite32be(0x4, &cpu1_ready); while(1) { #if 0 int val = inc; @@ -331,7 +374,8 @@ int kernel_main(void) // printk("\n"); // sched_yield(); - // cpu_relax(); +// printk("cpu1\n"); +// cpu_relax(); } //printk("%lld\n", buf[i]); diff --git a/kernel/clockevent.c b/kernel/clockevent.c index d2f73c970e23cedb9e262fed60b2adc08c13d551..2ba32ee40af1846f5fab172cf3935e350616239d 100644 --- a/kernel/clockevent.c +++ b/kernel/clockevent.c @@ -251,6 +251,32 @@ void clockevents_register_device(struct clock_event_device *dev) EXPORT_SYMBOL(clockevents_register_device); +/** + * @brief if an unused clock event device is available, offer it to the ticker + * + * @returns 0 on success, -ENODEV if no unused device was available + */ + +int clockevents_offer_device(void) +{ + struct clock_event_device *dev; + + list_for_each_entry(dev, &clockevent_devices, node) { + + if (dev->state != CLOCK_EVT_STATE_UNUSED) + continue; + + arch_local_irq_disable(); + tick_check_device(dev); + arch_local_irq_enable(); + + return 0; + } + + return -ENODEV; +} + + /** * @brief program a clock event * diff --git a/kernel/irq.c b/kernel/irq.c index b162072dd72b9ed6c446e052d884e56c145b4925..1d9104a70432a87f9c688fb5b517283dda73084c 100644 --- a/kernel/irq.c +++ b/kernel/irq.c @@ -84,8 +84,26 @@ int irq_exec_deferred(void) return 0; } +EXPORT_SYMBOL(irq_exec_deferred); +/** + * @brief set CPU affinity to a particular CPU (in SMP) + */ + +int irq_set_affinity(unsigned int irq, int cpu) +{ + if (!irq_ctrl) + return -EINVAL; + + if (irq_ctrl->irq_set_affinity) + irq_ctrl->irq_set_affinity(irq, cpu); + + return 0; + +} +EXPORT_SYMBOL(irq_set_affinity); + /** * @brief initialise the IRQ system */ diff --git a/kernel/kthread.c b/kernel/kthread.c index ceb31f8267b7a7de8be7e1049edca06fc61d2b00..ae16357677f6ac80e5b836c610edd4d0342359a0 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -39,18 +39,16 @@ struct remove_this_declaration /*{ static struct spinlock kthread_spinlock; -/** XXX dummy **/ -struct task_struct *kernel; - -struct thread_info *current_set[1]; +#include <asm/processor.h> +struct thread_info *current_set[2]; /* XXX */ /** * @brief lock critical kthread section */ -static inline void kthread_lock(void) + void kthread_lock(void) { spin_lock(&kthread_spinlock); } @@ -60,7 +58,7 @@ static inline void kthread_lock(void) * @brief unlock critical kthread section */ -static inline void kthread_unlock(void) +void kthread_unlock(void) { spin_unlock(&kthread_spinlock); } @@ -91,9 +89,9 @@ void sched_yield(void) { struct task_struct *tsk; - tsk = current_set[0]->task; - if (tsk->attr.policy == SCHED_EDF) - tsk->runtime = 0; + tsk = current_set[leon3_cpuid()]->task; +// if (tsk->attr.policy == SCHED_EDF) + tsk->runtime = 0; schedule(); } @@ -129,19 +127,22 @@ void kthread_wake_up(struct task_struct *task) } - +#include <asm/processor.h> struct task_struct *kthread_init_main(void) { struct task_struct *task; task = kmalloc(sizeof(*task)); +// printk("hi there, someone called %d from %lx\n", leon3_cpuid(), __builtin_return_address(0)); + if (!task) return ERR_PTR(-ENOMEM); /* XXX accessors */ task->attr.policy = SCHED_RR; /* default */ task->attr.priority = 1; + task->on_cpu = leon3_cpuid(); arch_promote_to_task(task); @@ -151,9 +152,7 @@ struct task_struct *kthread_init_main(void) arch_local_irq_disable(); kthread_lock(); - kernel = task; - /*** XXX dummy **/ - current_set[0] = &kernel->thread_info; + current_set[leon3_cpuid()] = &task->thread_info; task->state = TASK_RUN; @@ -226,6 +225,7 @@ static struct task_struct *kthread_create_internal(int (*thread_fn)(void *data), task->total = 0; task->slices = 0; + task->on_cpu = cpu; arch_init_task(task, thread_fn, data); task->state = TASK_NEW; diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 43a3ab42ae58fd43f13243bfc77bcaa8edd3a71f..093d17fad3a1020b53a7f88f74f2a38b7621d78b 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -16,6 +16,7 @@ #include <asm/switch_to.h> #include <string.h> +#include <asm/leon.h> #define MSG "SCHEDULER: " @@ -24,41 +25,56 @@ static LIST_HEAD(kernel_schedulers); /* XXX: per-cpu */ -extern struct thread_info *current_set[1]; - +extern struct thread_info *current_set[]; +ktime sched_last_time; +uint32_t sched_ev; + void kthread_lock(void); + void kthread_unlock(void); void schedule(void) { struct scheduler *sched; struct task_struct *next = NULL; + struct task_struct *prev = NULL; struct task_struct *current; - int64_t slot_ns; + int64_t slot_ns = 1000000LL; int64_t wake_ns = 1000000000; ktime rt; + ktime now; - static int once; - if (!once) { + static int once[2]; + if (!once[leon3_cpuid()]) { // tick_set_mode(TICK_MODE_PERIODIC); tick_set_next_ns(1e9); /* XXX default to 1s ticks initially */ - once = 1; + once[leon3_cpuid()] = 1; return; } + arch_local_irq_disable(); - /* kthread_lock(); */ +// if (leon3_cpuid() != 0) +// printk("cpu %d\n", leon3_cpuid()); +kthread_lock(); /* get the current task for this CPU */ - current = current_set[0]->task; + /* XXX leon3_cpuid() should be smp_cpu_id() arch call*/ + current = current_set[leon3_cpuid()]->task; + +// if (leon3_cpuid() != 0) +// if (current) +// printk("current %s\n", current->name); - rt = ktime_sub(ktime_get(), current->exec_start); + now = ktime_get(); + + rt = ktime_sub(now, current->exec_start); /** XXX need timeslice_update callback for schedulers */ /* update remaining runtime of current thread */ @@ -66,6 +82,8 @@ void schedule(void) current->runtime = ktime_sub(current->runtime, rt); current->total = ktime_add(current->total, rt); + current->state = TASK_RUN; +// current->runtime = 0; retry: @@ -74,9 +92,8 @@ retry: /* XXX: for now, try to wake up any threads not running * this is a waste of cycles and instruction space; should be * done in the scheduler's code (somewhere) */ - list_for_each_entry(sched, &kernel_schedulers, node) { - sched->wake_next_task(&sched->tq); - } + list_for_each_entry(sched, &kernel_schedulers, node) + sched->wake_next_task(&sched->tq, now); /* XXX need sorted list: highest->lowest scheduler priority, e.g.: @@ -91,7 +108,8 @@ retry: /* if one of the schedulers have a task which needs to run now, * next is non-NULL */ - next = sched->pick_next_task(&sched->tq); +retry2: + next = sched->pick_next_task(&sched->tq, now); #if 0 if (next) @@ -122,7 +140,31 @@ retry: */ if (next) { - slot_ns = next->sched->timeslice_ns(next); +#if 0 + if (next->on_cpu != KTHREAD_CPU_AFFINITY_NONE) { + if (next->on_cpu != leon3_cpuid()) { + // printk("%s on_cpu: %d but am %d\n", next->name, next->on_cpu, leon3_cpuid()); + + if (prev == next) + continue; + + prev = next; + next = NULL; + goto retry2; + } + // else + // printk("yay %s on_cpu: %d and am %d\n", next->name, next->on_cpu, leon3_cpuid()); + } + + if (next->sched) { +#endif + slot_ns = next->sched->timeslice_ns(next); +#if 0 + if (slot_ns < 0) + printk("<0 ! %s\n", next->name); + } + else continue; +#endif /* we found something to execute, off we go */ break; } @@ -132,39 +174,42 @@ retry: if (!next) { /* there is absolutely nothing nothing to do, check again later */ tick_set_next_ns(wake_ns); + kthread_unlock(); goto exit; } +// if (leon3_cpuid() != 0) +// printk("next %s\n", next->name); /* see if the remaining runtime in a thread is smaller than the wakeup * timeout. In this case, we will restrict ourselves to the remaining * runtime. This is particularly needeed for strictly periodic * schedulers, e.g. EDF */ - wake_ns = sched->task_ready_ns(&sched->tq); + wake_ns = sched->task_ready_ns(&sched->tq, now); - if (wake_ns < slot_ns) - slot_ns = wake_ns; + if (wake_ns > 0) + if (wake_ns < slot_ns) + slot_ns = wake_ns; - /* statistics */ + /* ALWAYS get current time here */ next->exec_start = ktime_get(); -// printk("at %llu\n", next->exec_start); - -// if (next) -// printk("real next %s %llu %llu\n", next->name, next->exec_start, slot_ns); - /* kthread_unlock(); */ -// printk("wake %llu\n", ktime_to_us(slot_ns)); /* subtract readout overhead */ - tick_set_next_ns(ktime_sub(slot_ns, 2000LL)); + tick_set_next_ns(ktime_sub(slot_ns, 1000LL)); + #if 1 if (slot_ns < 19000UL) { - // printk("wake %llu slot %llu %s\n", wake_ns, slot_ns, next->name); + printk("wake %lld slot %lld %s\n", wake_ns, slot_ns, next->name); + now = ktime_get(); goto retry; BUG(); } + sched_ev++; + sched_last_time = ktime_add(sched_last_time, ktime_delta(ktime_get(), now)); #endif + kthread_unlock(); prepare_arch_switch(1); switch_to(next); diff --git a/kernel/sched/edf.c b/kernel/sched/edf.c index 8d57bb71b00b10bf812f1d5b3ccd20d7e87d5ed6..89c678816cea57f652e9e6b9b92711f25fb3aba2 100644 --- a/kernel/sched/edf.c +++ b/kernel/sched/edf.c @@ -24,12 +24,11 @@ void sched_print_edf_list_internal(struct task_queue *tq, ktime now) struct task_struct *tmp; -// now = ktime_get(); ktime prev = 0; ktime prevd = 0; printk("\nktime: %lld\n", ktime_to_us(now)); - printk("S\tDeadline\tWakeup\tdelta W\tdelta P\tt_rem\ttotal\tslices\tName\t|\tdeltaw\tdeltad\twake0\tdead0\texecstart\n"); + printk("S\tDeadline\tWakeup\t\tdelta W\tdelta P\tt_rem\ttotal\tslices\tName\twcet\tavg\n"); printk("---------------------------------------------------------------------------------------------------------------------------------\n"); list_for_each_entry_safe(tsk, tmp, &tq->run, node) { @@ -46,15 +45,16 @@ void sched_print_edf_list_internal(struct task_queue *tq, ktime now) if (tsk->state == TASK_RUN) state = 'R'; - printk("%c\t%lld\t\t%lld\t%lld\t%lld\t%lld\t%lld\t%d\t%s\t|\t%lld\t%lld\t%lld\t%lld\t%lld\n", - state, ktime_to_us(tsk->deadline), ktime_to_us(tsk->wakeup), - ktime_to_us(rel_wait), ktime_to_us(rel_deadline), ktime_to_us(tsk->runtime), ktime_to_us(tsk->total), + if (tsk->slices == 0) + tsk->slices = 1; + + printk("%c\t%lld\t%lld\t\t%lld\t%lld\t%lld\t%lld\t%d\t%s\t|\t%lld\t%lld\n", + state, ktime_to_ms(tsk->deadline), ktime_to_ms(tsk->wakeup), + ktime_to_ms(rel_wait), ktime_to_ms(rel_deadline), ktime_to_ms(tsk->runtime), ktime_to_ms(tsk->total), tsk->slices, tsk->name, - ktime_us_delta(prev, tsk->wakeup), - ktime_us_delta(prevd, tsk->deadline), - ktime_to_us(tsk->first_wake), - ktime_to_us(tsk->first_dead), - ktime_to_us(tsk->exec_start)); + ktime_to_ms(tsk->attr.wcet), + ktime_to_ms(tsk->total/tsk->slices)); + prev = tsk->wakeup; prevd = tsk->deadline; } @@ -106,8 +106,9 @@ static inline bool schedule_edf_can_execute(struct task_struct *tsk, ktime now) if (tsk->runtime <= 0) return false; + if (ktime_before(tsk->deadline, now)) { - sched_print_edf_list_internal(&tsk->sched->tq, ktime_get()); + sched_print_edf_list_internal(&tsk->sched->tq, now); printk("%s violated, %lld %lld, dead %lld wake %lld now %lld start %lld\n", tsk->name, tsk->runtime, ktime_us_delta(tsk->deadline, now), tsk->deadline, tsk->wakeup, now, tsk->exec_start); @@ -127,9 +128,6 @@ static inline bool schedule_edf_can_execute(struct task_struct *tsk, ktime now) return true; - - - return false; } @@ -216,10 +214,8 @@ static int edf_schedulable(struct task_queue *tq, const struct task_struct *task /* add all in wakeup */ - struct task_struct *tmp2; if (!list_empty(&tq->wake)) { list_for_each_entry_safe(tsk, tmp, &tq->wake, node) - u += (double) (int32_t) tsk->attr.wcet / (double) (int32_t) tsk->attr.period; @@ -228,14 +224,13 @@ static int edf_schedulable(struct task_queue *tq, const struct task_struct *task /* add all running */ if (!list_empty(&tq->run)) { list_for_each_entry_safe(tsk, tmp, &tq->run, node) - u += (double) (int32_t) tsk->attr.wcet / (double) (int32_t) tsk->attr.period; } - if (u > 1.0) { + if (u > 1.9) { printk("I am NOT schedul-ableh: %f ", u); BUG(); return -EINVAL; @@ -246,61 +241,56 @@ static int edf_schedulable(struct task_queue *tq, const struct task_struct *task } + /* TODO check against projected interrupt rate, we really need a limit + * there */ + return 0; } -/** XXX **/ -static int64_t slot; - - - - -static struct task_struct *edf_pick_next(struct task_queue *tq) +#include <asm/processor.h> +static struct task_struct *edf_pick_next(struct task_queue *tq, ktime now) { -#define SOME_DEFAULT_TICK_PERIOD_FOR_SCHED_MODE 111111LL int64_t delta; - struct task_struct *go = NULL; struct task_struct *tsk; struct task_struct *tmp; struct task_struct *first; - ktime now = ktime_get(); - slot = 1000000000000; //SOME_DEFAULT_TICK_PERIOD_FOR_SCHED_MODE; + static int cnt; + + if (list_empty(&tq->run)) + return NULL; list_for_each_entry_safe(tsk, tmp, &tq->run, node) { /* time to wake up yet? */ delta = ktime_delta(tsk->wakeup, now); - if (delta > 0) { - - /* nope, optionally adjust slice */ - - if (delta < slot) - slot = delta; - + if (delta > 0) continue; - } + + /* XXX ok: what we need here: are multiple queues: one + * where tasks are stored which are currently able to + * run, here we need one per affinity and one generic one + * + * one where tasks are stored which are currently idle. + * tasks move to the idle queue when they cannot execute anymore + * and are moved from idle to run when their wakeup time has + * passed + */ /* if it's already running, see if there is time remaining */ if (tsk->state == TASK_RUN) { - +#if 0 + if (cnt++ > 10) { + sched_print_edf_list_internal(&tsk->sched->tq, now); + BUG(); + } +#endif if (!schedule_edf_can_execute(tsk, now)) { schedule_edf_reinit_task(tsk, now); - delta = ktime_delta(tsk->wakeup, now); - - /* if wakeup must happen earlier than the next - * scheduling event, adjust the slice - */ - - if (delta < slot) - slot = delta; - - BUG_ON(delta < 0); - /* always queue it up at the tail */ list_move_tail(&tsk->node, &tq->run); } @@ -312,12 +302,15 @@ static struct task_struct *edf_pick_next(struct task_queue *tq) first = list_first_entry(&tq->run, struct task_struct, node); + if (tsk->on_cpu == KTHREAD_CPU_AFFINITY_NONE + || tsk->on_cpu == leon3_cpuid()) { if (ktime_before (tsk->wakeup, now)) { if (ktime_before (tsk->deadline - tsk->runtime, first->deadline)) { tsk->state = TASK_RUN; list_move(&tsk->node, &tq->run); } } + } continue; } @@ -326,70 +319,122 @@ static struct task_struct *edf_pick_next(struct task_queue *tq) if (tsk->state == TASK_IDLE) { tsk->state = TASK_RUN; + BUG_ON(ktime_delta(tsk->wakeup, now) > 0); + /* if our deadline is earlier than the deadline at the * head of the list, move us to top */ + if (tsk->on_cpu == KTHREAD_CPU_AFFINITY_NONE + || tsk->on_cpu == leon3_cpuid()) { first = list_first_entry(&tq->run, struct task_struct, node); + if (first->on_cpu != KTHREAD_CPU_AFFINITY_NONE + || tsk->on_cpu != leon3_cpuid()) { + list_move(&tsk->node, &tq->run); + } + if (ktime_before (tsk->deadline - tsk->runtime, first->deadline)) list_move(&tsk->node, &tq->run); + } continue; } } - first = list_first_entry(&tq->run, struct task_struct, node); - delta = ktime_delta(first->wakeup, now); +/* XXX argh, need cpu affinity run lists */ + list_for_each_entry_safe(tsk, tmp, &tq->run, node) { + if (tsk->state == TASK_RUN) { - if (first->state == TASK_RUN) { - go = first; - slot = first->runtime; - } + if (tsk->on_cpu == KTHREAD_CPU_AFFINITY_NONE + || tsk->on_cpu == leon3_cpuid()) { + tsk->state = TASK_BUSY; + return tsk; + } else { + continue; + } + } + } -#if 1 /* XXX should not be needed, but needs verification! */ - if (!go) { - list_for_each_entry_safe(tsk, tmp, &tq->run, node) { +#if 0 + first = list_first_entry(&tq->run, struct task_struct, node); + delta = ktime_delta(first->wakeup, now); - if (tsk->state != TASK_RUN) - continue; - if (ktime_before (tsk->wakeup, now)) { - go = tsk; - slot = tsk->runtime; - break; - } - } + if (first->state == TASK_RUN) { + // printk("c %d\n", leon3_cpuid()); + first->state = TASK_BUSY; + return first; } #endif - return go; + return NULL; } -static void edf_wake_next(struct task_queue *tq) +static void edf_wake_next(struct task_queue *tq, ktime now) { ktime last; struct task_struct *tmp; struct task_struct *task; + struct task_struct *first = NULL; + struct task_struct *t; + struct task_struct *prev = NULL; + + ktime wake; if (list_empty(&tq->wake)) return; - last = ktime_get(); - + last = now; +#if 0 /* OK */ list_for_each_entry_safe(task, tmp, &tq->run, node) { if (last < task->deadline) last = task->deadline; } +#endif +#if 1 /* better */ task = list_entry(tq->wake.next, struct task_struct, node); + + list_for_each_entry_safe(t, tmp, &tq->run, node) { + + first = list_first_entry(&tq->run, struct task_struct, node); + if (ktime_before (t->wakeup, now)) { + if (ktime_before (t->deadline - t->runtime, first->deadline)) { + list_move(&t->node, &tq->run); + } + } + } + + list_for_each_entry_safe(t, tmp, &tq->run, node) { + + /* if the relative deadline of task-to-wake can fit in between the unused + * timeslice of this running task, insert after the next wakeup + */ + if (task->attr.deadline_rel < ktime_sub(t->deadline, t->wakeup)) { + //last = ktime_add(t->deadline, t->attr.period); + last = t->wakeup; + + + break; + } + + if (task->attr.wcet < ktime_sub(t->deadline, t->wakeup)) { + //last = ktime_add(t->deadline, t->attr.period); + last = t->deadline; + + break; + } + } +#endif + task->state = TASK_IDLE; /* initially furthest deadline as wakeup */ @@ -490,19 +535,31 @@ error: /* called after pick_next() */ -ktime edf_task_ready_ns(struct task_queue *tq) +ktime edf_task_ready_ns(struct task_queue *tq, ktime now) { int64_t delta; + struct task_struct *first; struct task_struct *tsk; struct task_struct *tmp; - ktime wake = 123456789123LL; - ktime now = ktime_get(); + ktime slice = 12345679123ULL; + ktime wake = 123456789123ULL; - list_for_each_entry_safe(tsk, tmp, &tq->run, node) { + list_for_each_entry_safe(first, tmp, &tq->run, node) { + if (first->state != TASK_RUN) + continue; + + slice = first->runtime; + break; + } + list_for_each_entry_safe(tsk, tmp, &tq->run, node) { +#if 0 + if (tsk->state == TASK_BUSY) + continue; /*meh */ +#endif delta = ktime_delta(tsk->wakeup, now); if (delta <= 0) @@ -512,13 +569,12 @@ ktime edf_task_ready_ns(struct task_queue *tq) wake = delta; } - if (slot > wake) - slot = wake; - + if (slice > wake) + slice = wake; - BUG_ON(slot <= 0); + BUG_ON(slice <= 0); - return slot; + return slice; } diff --git a/kernel/sched/rr.c b/kernel/sched/rr.c index 2cf78792420c8a88d9580f89bf9dcb7432b35ef2..9cd49deed605b67c32094a08290560494fc1a185 100644 --- a/kernel/sched/rr.c +++ b/kernel/sched/rr.c @@ -12,17 +12,21 @@ #define MSG "SCHED_RR: " - -static struct task_struct *rr_pick_next(struct task_queue *tq) +#include <asm/processor.h> +static struct task_struct *rr_pick_next(struct task_queue *tq, ktime now) { struct task_struct *next = NULL; + struct task_struct *tmp; + + if (list_empty(&tq->run)) + return NULL; - while (!list_empty(&tq->run)) { + list_for_each_entry_safe(next, tmp, &tq->run, node) { - next = list_entry(tq->run.next, struct task_struct, node); - BUG_ON(!next); + if (next->on_cpu == KTHREAD_CPU_AFFINITY_NONE + || next->on_cpu == leon3_cpuid()) { if (next->state == TASK_RUN) { /* XXX: must pick head first, then move tail on put() @@ -34,7 +38,8 @@ static struct task_struct *rr_pick_next(struct task_queue *tq) /* reset runtime */ next->runtime = (next->attr.priority * tick_get_period_min_ns()); - break; + + } if (next->state == TASK_IDLE) @@ -43,6 +48,17 @@ static struct task_struct *rr_pick_next(struct task_queue *tq) if (next->state == TASK_DEAD) list_move_tail(&next->node, &tq->dead); + break; + + + } else { + next = NULL; + continue; + } + + + + } @@ -51,7 +67,7 @@ static struct task_struct *rr_pick_next(struct task_queue *tq) /* this sucks, wrong place. keep for now */ -static void rr_wake_next(struct task_queue *tq) +static void rr_wake_next(struct task_queue *tq, ktime now) { struct task_struct *task; @@ -94,7 +110,7 @@ static void rr_enqueue(struct task_queue *tq, struct task_struct *task) static ktime rr_timeslice_ns(struct task_struct *task) { - return (ktime) (task->attr.priority * tick_get_period_min_ns()); + return (ktime) (task->attr.priority * tick_get_period_min_ns() * 50); } @@ -129,7 +145,7 @@ static int rr_check_sched_attr(struct sched_attr *attr) * so this function always returns 0 */ -ktime rr_task_ready_ns(struct task_queue *tq) +ktime rr_task_ready_ns(struct task_queue *tq, ktime now) { return (ktime) 0ULL; } diff --git a/kernel/tick.c b/kernel/tick.c index 4a6e97b961c17e9a50ca9d3bd6488515b704db45..582176da1e9ebc2260ae021ace96f5ca69e7b299 100644 --- a/kernel/tick.c +++ b/kernel/tick.c @@ -16,16 +16,18 @@ #include <kernel/export.h> #include <kernel/clockevent.h> #include <kernel/kthread.h> +#include <kernel/irq.h> #define MSG "TICK: " /* the minimum effective tick period; default to 1 ms */ static unsigned long tick_period_min_ns = 1000000UL; -static struct clock_event_device *tick_device; - +/* XXX */ +static struct clock_event_device *tick_device[2]; +#include <asm/processor.h> static void tick_event_handler(struct clock_event_device *dev) { @@ -35,13 +37,13 @@ static void tick_event_handler(struct clock_event_device *dev) struct clock_event_device *tick_get_device(__attribute__((unused)) int cpu) { - return tick_device; + return tick_device[cpu]; } void tick_set_device(struct clock_event_device *dev, __attribute__((unused)) int cpu) { - tick_device = dev; + tick_device[cpu] = dev; } /** @@ -134,7 +136,7 @@ static void tick_calibrate_min(struct clock_event_device *dev) * @brief configure the tick device */ -static void tick_setup_device(struct clock_event_device *dev) +static void tick_setup_device(struct clock_event_device *dev, int cpu) { tick_calibrate_min(dev); @@ -160,7 +162,7 @@ void tick_check_device(struct clock_event_device *dev) return; /* XXX need per-cpu selection later */ - cur = tick_get_device(0); + cur = tick_get_device(leon3_cpuid()); if (!tick_check_preferred(cur, dev)) return; @@ -168,9 +170,12 @@ void tick_check_device(struct clock_event_device *dev) clockevents_exchange_device(cur, dev); /* XXX as above */ - tick_set_device(dev, 0); + tick_set_device(dev, leon3_cpuid()); + + /* XXX as above */ + tick_setup_device(dev, leon3_cpuid()); - tick_setup_device(dev); + irq_set_affinity(dev->irq, leon3_cpuid()); /* XXX should inform scheduler to recalculate any deadline-related * timeouts of tasks */ @@ -180,7 +185,8 @@ void tick_check_device(struct clock_event_device *dev) /** * @brief configure the mode of the ticker * - * @returns 0 on success, -EINVAL if mode not available + * @returns 0 on success, -EINVAL if mode not available, + * -ENODEV if no device is available for the current CPU */ int tick_set_mode(enum tick_mode mode) @@ -189,7 +195,9 @@ int tick_set_mode(enum tick_mode mode) /* XXX need per-cpu selection later */ - dev = tick_get_device(0); + dev = tick_get_device(leon3_cpuid()); + if (!dev) + return -ENODEV; switch(mode) { case TICK_MODE_PERIODIC: @@ -221,7 +229,8 @@ unsigned long tick_get_period_min_ns(void) /** * @brief configure next tick period in nanoseconds * - * returns 0 on success, 1 if nanoseconds range was clamped to clock range + * returns 0 on success, 1 if nanoseconds range was clamped to clock range, + * -ENODEV if no device is available for the current CPU */ @@ -231,7 +240,9 @@ int tick_set_next_ns(unsigned long nanoseconds) /* XXX need per-cpu selection later */ - dev = tick_get_device(0); + dev = tick_get_device(leon3_cpuid()); + if (!dev) + return -ENODEV; return clockevents_program_timeout_ns(dev, nanoseconds); } @@ -255,7 +266,9 @@ int tick_set_next_ktime(struct timespec expires) /* XXX need per-cpu selection later */ - dev = tick_get_device(0); + dev = tick_get_device(leon3_cpuid()); + if (!dev) + return -ENODEV; return clockevents_program_event(dev, expires); } diff --git a/lib/string.c b/lib/string.c index 271190181c553240f51a4ae4ef78b24e0c4efc10..d4d52f409e9c09eec85b275f0032013da173807b 100644 --- a/lib/string.c +++ b/lib/string.c @@ -721,3 +721,118 @@ long int strtol(const char *nptr, char **endptr, int base) } +/** + * @brief convert a string to a long long integer + * + * @param nptr the pointer to a string (possibly) representing a number + * @param endptr if not NULL, stores the pointer to the first character not part + * of the number + * @param base the base of the number string + * + * @return the converted number + * + * @note A base of 0 defaults to 10 unless the first character after + * ' ', '+' or '-' is a '0', which will default it to base 8, unless + * it is followed by 'x' or 'X' which defaults to base 16. + * If a number is not preceeded by a sign, it is assumed to be a unsigned + * type and may therefore assume the size of ULLONG_MAX. + * If no number has been found, the function will return 0 and + * nptr == endptr + * + * @note if the value would over-/underflow, LLONG_MAX/MIN is returned + */ + +long long int strtoll(const char *nptr, char **endptr, int base) +{ + int neg = 0; + + unsigned long long res = 0; + unsigned long long clamp = (unsigned long) ULONG_LONG_MAX; + + + + if (endptr) + (*endptr) = (char *) nptr; + + for (; isspace(*nptr); nptr++); + + + if ((*nptr) == '-') { + nptr++; + neg = 1; + clamp = -(unsigned long long) LONG_LONG_MIN; + } else if ((*nptr) == '+') { + clamp = (unsigned long long) LONG_LONG_MAX; + nptr++; + } + + if (!base || base == 16) { + if ((*nptr) == '0') { + switch (nptr[1]) { + case 'x': + case 'X': + nptr += 2; + base = 16; + break; + default: + nptr++; + base = 8; + break; + } + } else { + /* default */ + base = 10; + } + } + + + /* Now iterate over the string and add up the digits. We convert + * any A-Z to a-z (offset == 32 in ASCII) to check if the string + * contains letters representing values (A=10...Z=35). We abort if + * the computed digit value exceeds the given base or on overflow. + */ + + while (1) { + unsigned int c = (*nptr); + unsigned int l = c | 0x20; + unsigned int val; + + if (isdigit(c)) + val = c - '0'; + else if (islower(l)) + val = l - 'a' + 10; + else + break; + + if (val >= base) + break; + + /* Check for overflow only if result is approximately within + * range of it, given the base. If we overflow, set result + * to clamp (LLONG_MAX or LLONG_MIN) + */ + + if (res & (~0ULL << (BITS_PER_LONG_LONG - fls(base)))) { + if (res > (clamp - val) / base) { + res = clamp; + break; + } + } + + res = res * base + val; + nptr++; + } + + + /* update endptr if a value was found */ + if (res) + if (endptr) + (*endptr) = (char *) nptr; + + + if (neg) + return - (long long int) res; + + return (long long int) res; +} + diff --git a/lib/vsnprintf.c b/lib/vsnprintf.c index 5bfe4adbd153be73a1da50cf40edb420129a94d6..2f32fabcec79c0d2d7853657a692c33fdf86d564 100644 --- a/lib/vsnprintf.c +++ b/lib/vsnprintf.c @@ -1310,7 +1310,7 @@ int vsnprintf(char *str, size_t size, const char *format, va_list ap) if (size) { if (str) { if (buf < end) - buf[-1] = '\0'; + buf[0] = '\0'; else end[-1] = '\0'; }