Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cpu/atmega_common: __exit_isr thread_yield #8988

Merged
merged 6 commits into from
Jun 26, 2018
Merged

Conversation

Josar
Copy link
Contributor

@Josar Josar commented Apr 19, 2018

As proposed by @gebart in #8979 this PR adds thread_yield(); to __exit_isr similar to #6171.

This avoids duplicated code.
Additionaly code was uncrustified.

@Josar Josar force-pushed the atmega_isr branch 2 times, most recently from a0a1515 to 0087952 Compare April 19, 2018 21:59
@Josar Josar changed the title cpu/atmega_common: uncrustified cpu/atmega_common: __exit_isr thread_yield Apr 20, 2018
Copy link
Member

@jnohlgard jnohlgard left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good improvement! For consistency, I would prefer to let the enter/exit isr calls in the UART driver to remain in each ISR instead of the central IRQ handler.

}

#ifdef UART_0_ISR
ISR(UART_0_ISR, ISR_BLOCK)
{
__enter_isr();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer the original design where enter isr is the first line of each ISR

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this just a preference or are there any reasons. As i see there is no benefit in settings the __in_isr flag as early as possible, as there is no chance that code could be invoked which could use this information.
Setting the flag prevents code in the callback to sleep or do other things which schould not be done in interrupts, so setting it before the callback should be sufficient, imho.

I change it as it is like this in all other interrupt handler and thought it would also increase consistency.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personal preference. The consistency helps when reviewing the code and also reduces the risk of missing it if someone uses this periph driver as a template for some other driver for the same CPU.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Copy link
Contributor Author

@Josar Josar Apr 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gebart maybe we just have a language problem as consistency translates into two different meanings in german, depending of the context. Do you mean consistency in regard as it stays as it was or consistency as it is similar to other code of the atmegas?

static inline void irq_handler(uint8_t pin_num)
{
__enter_isr();
config[pin_num].cb(config[pin_num].arg);
__exit_isr();
}
ISR(INT0_vect, ISR_BLOCK)
{
irq_handler(0); /**< predefined interrupt pin */
}

#ifdef TIMER_NUMOF
static inline void _isr(tim_t tim, int chan)
{
__enter_isr();
*ctx[tim].mask &= ~(1 << (chan + OCIE1A));
ctx[tim].cb(ctx[tim].arg, chan);
if (sched_context_switch_request) {
thread_yield();
}
__exit_isr();
}
#endif
#ifdef TIMER_0
ISR(TIMER_0_ISRA, ISR_BLOCK)
{
_isr(0, 0);
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you are right regarding consistency. I am not very engaged in the atmega development, sorry.
What I was trying to push for is to have a strict template for the ISR definitions which should be the same across all drivers for the CPU family, which you showed that this is. Consistency makes refactoring the driver code easier in the future. I am happy as long as this design matches the other periph drivers for the atmega.

@kYc0o kYc0o self-assigned this Apr 20, 2018
@kYc0o kYc0o added Type: enhancement The issue suggests enhanceable parts / The PR enhances parts of the codebase / documentation Platform: AVR Platform: This PR/issue effects AVR-based platforms labels Apr 20, 2018
@kYc0o kYc0o added this to the Release 2018.07 milestone Apr 20, 2018
Copy link
Contributor

@kaspar030 kaspar030 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please split out the uncrustify commit.

@@ -75,20 +81,20 @@ void cpu_init(void);
/**
* @brief Print the last instruction's address
*/
__attribute__((always_inline)) static inline void cpu_print_last_instruction(void)
static inline void __attribute__((always_inline)) cpu_print_last_instruction(void)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unrelated

{
uint8_t hi;
uint8_t lo;
uint16_t ptr;

__asm__ volatile( "in __tmp_reg__, __SP_H__ \n\t"
__asm__ volatile ("in __tmp_reg__, __SP_H__ \n\t"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unrelated

@@ -34,8 +34,10 @@
#include <stdint.h>

#include <avr/interrupt.h>
#include "board.h"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this include actually used?

@jnohlgard
Copy link
Member

My concerns have been addressed.

@Josar
Copy link
Contributor Author

Josar commented Apr 25, 2018

@kaspar030 can you approve it like this?

@Josar
Copy link
Contributor Author

Josar commented Apr 27, 2018

@kaspar030 ping 😏

*/
static inline void __exit_isr(void)
static inline void __attribute__((always_inline)) __exit_isr(void)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why always inline?

Copy link
Contributor Author

@Josar Josar Apr 27, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As it will be executed in an ISR, i thought speed could matter.
And if optimization is off it won't

GCC does not inline any functions when not optimizing unless you specify the ‘always_inline’ attribute for the function, like this:

/* Prototype.  */
inline void foo (const char) __attribute__((always_inline));

But as the statement is the attribute has to be after the function, i am not sure but, i think it does not matter.

This benchmark makes me think maybe inline only is better.

*/
static inline void __enter_isr(void)
static inline void __attribute__((always_inline)) __enter_isr(void)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why always inline? Don't trust the compiler?

@Josar
Copy link
Contributor Author

Josar commented May 7, 2018

@gebart and @kaspar030 rebased onto #8904 and added neccessary changes.

@ZetaR60
Copy link
Contributor

ZetaR60 commented May 7, 2018

This looks okay to me, but note that it introduces a yield in the periph/gpio.c ISR where there previously was none.

@Josar
Copy link
Contributor Author

Josar commented May 7, 2018

@ZetaR60 i think that is not fully correct, it request a thread yield if there was a sched_context_switch_request in the callback handler. Which i would expect to happen, as the __in_isr blocks the context switch and at the end of an isr it has to be executed. Otherwise it would be lost or executed with an other thread_yield.

But thinking further is it neccessary to duplicate the code?

static inline void __exit_isr(void)
{
     if (sched_context_switch_request) {
         thread_yield();
        thread_yield_isr();
     }
   __in_isr = 0;
}

thread_yield_isr(); is the same as thread_yield_higher(); so just bringing __in_isr = 0 before the if and setting reti to the end will have the same result with less code, or not ?

static inline void __exit_isr(void)
{
     __in_isr = 0;
     if (sched_context_switch_request) {
         thread_yield();
     }
    __asm__ volatile ("reti");
}

@ZetaR60
Copy link
Contributor

ZetaR60 commented May 8, 2018

Yes, you are right about my comment being erroneous. Sorry for the lazy thought process.

There are problems with moving __asm__ volatile("reti"); to a different area from __context_restore(); because the context change directly manipulates the program counter, and returning from a function also manipulates the program counter. You would have to add __attribute__((always_inline)) inline to thread_yield / thread_yield_isr, which means you would have a copy of all the contents of __context_save and __context_restore in every ISR.

@Josar
Copy link
Contributor Author

Josar commented May 8, 2018

@ZetaR60 seems it was too late for my brain, of course moving reti does not work.

Maybe we could agree on just putting the content of the if to thread_yield_isr();` ?

I don`t imagine a situation where saving the context and running the scheduler is necessary if there was not a thread change request.

Further thoughts, I think there should be a function like sched_thread_change_needed to avoid saving and restoring the context when a yield or yield higher is issued but there is no higher.
As of now sched_run checks this, after the context was allready saved.

@ZetaR60
Copy link
Contributor

ZetaR60 commented May 8, 2018

@Josar can you be a bit more precise which things you are referring to? Currently nothing is run at the end of an ISR if there was no attempt to yield within the callback, because sched_context_switch_request is not set. Are the other things you are mentioning proposed changes to core/thread.c ? Currently this PR is a refactor; perhaps discussion of changes of behavior should be moved to another PR specific to that change? There might be some improvements to be made in the way I rewrote context swapping, but that rewrite took a lot of testing to do properly.

@Josar
Copy link
Contributor Author

Josar commented May 8, 2018

@ZetaR60 yes this PR should just be an refactorization.

  1. thing i refer is changing the code to this
void thread_yield_isr(void) {
    if (sched_context_switch_request) {
        thread_yield();
         __context_save();
         sched_run();
        __context_restore();
    }
    __asm__ volatile("reti");
}

static inline void __exit_isr(void)
{
    thread_yield_isr();
     __in_isr = 0;
}
  1. thing is just an idea which could be a improvement.
    Now when a tread issues a thread_yield_higher e.g. when sending a msg, but the running thread has a higher priority then the thread receiving the msg, this will cause a context save, as the check if the context has to be change is done in sched_run and then the restore the same thread. this could be prevented, by first checking if the context has to be changed, then save, change, restore.

@ZetaR60
Copy link
Contributor

ZetaR60 commented May 8, 2018

Those sound like reasonable changes. They would involve changing behavior though, so the extensive testing done in #8904 probably ought to be re-checked in a PR that changes it.

@Josar
Copy link
Contributor Author

Josar commented May 15, 2018

@ZetaR60 thinking further i think this can be optimized.

After deciding that a context swap is allowed in an ISR there is no need of preventing an context swap in an ISR, the only thing which has to be taken care of is to return with reti. The rest are remains which only add overhead.

As it is now we check for isr set the flag, return to caller, call next function, check for flag and do the swap.
Removing this will not only encrease swapping in interrupts but also increase interrupts with no yield as the flag check is removed.

void thread_yield_higher(void) {
        __context_save();
        sched_run();
        __context_restore();

    if (irq_is_in() == 0) {
        __asm__ volatile("ret");
    } else {
        __exit_isr();
        __asm__ volatile ("reti");
    }
}

and removing the if irq in the scheduler

    if (!on_runqueue || (current_prio > other_prio)) {
        // if (irq_is_in()) {
        //     DEBUG("sched_switch: setting sched_context_switch_request.\n");
        //     sched_context_switch_request = 1;
        // }
        // else {
            DEBUG("sched_switch: yielding immediately.\n");
            thread_yield_higher();
        // }
    }
    else {
        DEBUG("sched_switch: continuing without yield.\n");
    }

first test shows expected results.

  1. test/xtimer_msg ok
  2. sched_testing ok
  3. mutex_order ok
  4. posix_semaphore still falis test 4
  5. thread_flags seems ok

The question is are there platforms which can not switch the context in an ISR?

@Josar
Copy link
Contributor Author

Josar commented May 15, 2018

@kYc0o would you mind having a look at this?

@ZetaR60
Copy link
Contributor

ZetaR60 commented May 15, 2018

@Josar This change to thread_yield_higher is a pretty major change in behavior. Could you create another PR for it?

@Josar Josar mentioned this pull request May 15, 2018
@Josar
Copy link
Contributor Author

Josar commented May 29, 2018

@kaspar030 maybe you could find some time?

@kYc0o
Copy link
Contributor

kYc0o commented May 29, 2018

Code wise looks OK. there are some "unrelated" changes due to uncrustify, but I guess they're valid. I need to test though. Will report back when I test on my available boards. @ZetaR60 can you also test on the mega-xplained? I guess timers and peripherals basic testing is enough.

@ZetaR60
Copy link
Contributor

ZetaR60 commented May 29, 2018

I ran some tests, and got a couple failures:

thread_basic			OK
thread_cooperation		OK
thread_exit			OK
thread_flags			OK
thread_flags_xtimer		Reports failure
thread_flood			OK
thread_msg			OK
thread_msg_block_wo_queue	OK
thread_msg_block_w_queue	OK
thread_msg_seq			OK
xtimer_hang			OK
xtimer_periodic_wakeup		OK
xtimer_remove			Reports failure
xtimer_reset			OK
xtimer_usleep			OK
xtimer_usleep_short		OK

I think we need a bit more extensive examination. @Josar I suggest rebasing on trunk and running tests side by side with your code and the exact same revision without your code.

@Josar
Copy link
Contributor Author

Josar commented May 29, 2018

Rebased and fixed.
All above Tests run successfull.
Besides that the output of xtimer_hang stutters, which is solved in #9211

PS: it would be nice to have a readme in each test with the expected output.

@ZetaR60
Copy link
Contributor

ZetaR60 commented May 30, 2018

Confirmed that the previously failed tests are now succeeding on mega-xplained. Also did another proofread of the changes, and they look fine. Let me know if I should run any other tests on mega-xplained.

@kYc0o kYc0o added the CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR label May 30, 2018
@Josar
Copy link
Contributor Author

Josar commented Jun 4, 2018

@kaspar030 any further remarks?

@Josar
Copy link
Contributor Author

Josar commented Jun 7, 2018

@kaspar030 could you have a second look?

@ZetaR60
Copy link
Contributor

ZetaR60 commented Jun 7, 2018

@Josar You are being a bit aggressive with pinging Kaspar. I know things often move slowly, but I don't think repeating requests that much is going to help.

@Josar
Copy link
Contributor Author

Josar commented Jun 8, 2018

Thank you @ZetaR60 for indicating this. Not ment to be agressiv at all.
I just tried to follow the advise in the wiki to remind the reviewer if there is no reaction for some days.

@kYc0o
Copy link
Contributor

kYc0o commented Jun 8, 2018

@Josar please squash.

@kYc0o
Copy link
Contributor

kYc0o commented Jun 8, 2018

All the tests in #8988 (comment) passed on arduino-mega2560.

@Josar Josar force-pushed the atmega_isr branch 2 times, most recently from c30bb2c to a8f37f4 Compare June 8, 2018 15:54
@Josar
Copy link
Contributor Author

Josar commented Jun 8, 2018

I uncrustified the touched files, hope this is allright.

@@ -87,17 +88,21 @@ int timer_init(tim_t tim, unsigned long freq, timer_cb_t cb, void *arg)
uint8_t pre = 0;

/* make sure given device is valid */
if (tim >= TIMER_NUMOF) {
if (tim >= TIMER_NUMOF)
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this change the result of uncrustify ? Then I think there's an issue with the uncrustify configuration file. The opening curly brace should be on the same line as the if.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems my vscode config got messed up.

@Josar
Copy link
Contributor Author

Josar commented Jun 15, 2018

@aabadie i think that the uncrustifying went right this time.
@kYc0o rebased and squashed.

@kYc0o
Copy link
Contributor

kYc0o commented Jun 26, 2018

Perfect! Thanks for your contribution!

@kYc0o kYc0o merged commit f0dce1b into RIOT-OS:master Jun 26, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Platform: AVR Platform: This PR/issue effects AVR-based platforms Type: enhancement The issue suggests enhanceable parts / The PR enhances parts of the codebase / documentation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants