LinuxQuestions.org
Visit Jeremy's Blog.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 08-23-2006, 02:28 PM   #1
rnkayab
LQ Newbie
 
Registered: Aug 2006
Posts: 5

Rep: Reputation: 0
Restarting an ISR in a transaction-like style


I am trying to implement restarting of an ISR from the beginning when it is interrupted by a higher priority handler (in Linux on an x86). To do so, I would like to set (while in the ISR) the instruction pointer (EIP) and offset (XCS) registers to point to the ISR so that it is restarted when it executes RET (It is ok to throw away the ISR's work). What is the best way to do this without erasing information about any handler that the IST itself has interrupted?

Code:
 
           /* regs is a pointer to registers saved when the interrupt occured */
void ISR(int irq, void *dev_id, struct pt_regs *regs)
{
/* do stuff here */

/* disable all interrupts */
if (was_interrupted) {
  long eip_;
  int xcs_;
  /* find where void ISR(...) is in memory (segment and offset) 
     and set registers on the stack */
  regs->eip = ?  /* BAD: this will rewrite saved data on the stack */
  regs->xcs = ?
                 /* perhaps jump to ISR? */
  }
/* enable all interrupts */

}
Thank you.

Last edited by rnkayab; 08-23-2006 at 02:33 PM.
 
Old 08-23-2006, 04:13 PM   #2
theNbomr
LQ 5k Club
 
Registered: Aug 2005
Distribution: OpenSuse, Fedora, Redhat, Debian
Posts: 5,399
Blog Entries: 2

Rep: Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908
I don't think you have a good understanding of interrupts, interrupt handling, and machine instructions. A properly crafted interrupt handler leaves no trace of it's execution, save for that which may be required for passing information to kernel and/or user-space code. That begs the question of how exactly you plan to detect that an interrupt has occurred during the execution of your ISR.

Setting of the XCS:EIP registers (the instruction pointer) is accomplished at the machine code level through instructions like JMPs, CALLs, RETs, and the likes of those instructions, as well as the normal incremental adjustments to the instruction pointer as it performs instruction opcode and immediate operand fetches.

Why don't you explain what you are really trying to accompish?

--- rod.
 
Old 08-23-2006, 05:19 PM   #3
rnkayab
LQ Newbie
 
Registered: Aug 2006
Posts: 5

Original Poster
Rep: Reputation: 0
> A properly crafted interrupt handler leaves no trace of it's execution, save for
> that which may be required for passing information to kernel and/or user-space code. > That begs the question of how exactly you plan to detect that an interrupt has
> occurred during the execution of your ISR.

Let's restrict myself to detecting interrupts from two drivers' ISRs that I have written. Suppose that one ISR, ISR1 runs at priority level 1 and the other, ISR2, runs at priority level 2. In addition I have some kernel code, Compute0 in both drivers which runs at the lowest level 0. I want something like this where each interrupt records its level when the handler runs:

Code:
volatile int interrupted; 
int counter; // global state

void Compute0()
{
  do {
    highestLevel = 0;
    // my work here  
    counter++; 
    // more of my work here 
  } while ( highestLevel > 0 );  /* if true, Compute0 was interrupted */
}

/* low-level 1 interrupt handler */
BOOLEAN Isr1 ()
{
  do {
    highestLevel = 1;
    // my work here
    counter++; 
    // more of my work here 
  } while ( highestLevel > 1 );  /* if true, Isr1 was interrupted */

  return TRUE;
}

/* level 2 interrupt handler */
BOOLEAN Isr2 ()
{
   highestLevel = 2;
}
So Compute0 and ISR1 would restart their work if interrupted. Restart also means that I roll-back counter and any work I have done, and start my work from the beginning (like commit in databases) (not shown in the code).

I am interested in extending this to detection of other interrupts which modify the global state "counter".

Last edited by rnkayab; 08-23-2006 at 05:23 PM.
 
Old 08-23-2006, 08:20 PM   #4
theNbomr
LQ 5k Club
 
Registered: Aug 2005
Distribution: OpenSuse, Fedora, Redhat, Debian
Posts: 5,399
Blog Entries: 2

Rep: Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908
It sounds like you are wanting to use interrupts as synchronizing events, which is the domain of semaphores. Not too sure if semaphores can be manipulated from an ISR, but probably can. That would allow the lowest overhead within the ISR, and allow you to run the majority of the work in user-space code. You do seem to have the logic expressed fairly clearly. The mechanics of implementing it by attaching it to interrupt sources is presumeably one of those 'exercises left to the reader'. Or was that actually your real question? The one possible flaw in your use of shared variables is that there is a possibility of your while loops executing one extra time, due to non-atomic fetch & test of the shared variable. Again, if this is a problem, then semaphores are a solution.

--- rod.
 
Old 08-23-2006, 08:58 PM   #5
rnkayab
LQ Newbie
 
Registered: Aug 2006
Posts: 5

Original Poster
Rep: Reputation: 0
> It sounds like you are wanting to use interrupts as synchronizing events, which is
> the domain of semaphores
Yes, it looks like that. However, I am not interested in an atomic section which cannot be interrupted (which locking/semaphores would do?). I am interested in handling the higher level event immediately (except the short delay in the "copy" and "commit" phases below):

Code:
volatile int interrupted; 
int counter;       // global state

void Compute0()
{
  do {
    highestLevel = 0;

    disable_interrupts();  // "copy" phase
    counter_t = counter;
    enable_interrupts();

    // my work here  
    counter_t++; // work on local temporary
    // more of my work here 
  } while ( highestLevel > 0 );  /* if true, Compute0 was interrupted */

  disable_interrupts();  // "commit" phase
  counter = counter_t;
  enable_interrupts();
}

/* low-level 1 interrupt handler */
BOOLEAN Isr1 ()
{
  int counter_t;

  do {
    highestLevel = 1;
  
    disable_interrupts();  // "copy" phase
    counter_t = counter;
    enable_interrupts();

    // my work here
    counter_t++; 
    // more of my work here 
  } while ( highestLevel > 1 );  /* if true, Isr1 was interrupted */

  disable_interrupts();  // "commit" phase
  counter = counter_t;
  enable_interrupts();

  return TRUE;
}

/* level 2 interrupt handler */
BOOLEAN Isr2 ()
{
   highestLevel = 2;
}
The above is the full implementation of what I want to. My question is how to detect if I was interrupted by some other interrupt handler that I haven't written (meaning it doesn't set the flag highestLevel = ...) that modifies the shared int counter.

> The one possible flaw in your use of shared variables is that there is a possibility > of your while loops executing one extra time, due to non-atomic fetch & test of the > shared variable
How? What is a scenario that would do that? I thought making the shared variable volatile would fetch the latest value. The only possibility I see is being interrupted while executing the while loop condition and not seeing the new value of the shared variable. That is ok with me as I have already done my work *WITHOUT* being interrupted.
 
Old 08-24-2006, 11:52 AM   #6
theNbomr
LQ 5k Club
 
Registered: Aug 2005
Distribution: OpenSuse, Fedora, Redhat, Debian
Posts: 5,399
Blog Entries: 2

Rep: Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908
Quote:
Originally Posted by rnkayab
>
The above is the full implementation of what I want to. My question is how to detect if I was interrupted by some other interrupt handler that I haven't written (meaning it doesn't set the flag highestLevel = ...) that modifies the shared int counter.
This cannot be possible. A concept central to correct interrupt processing is that it happens invisibly to all processes that are not part of the interrupt handler's private code and data space. How can an interrupt modify your private variable?

Quote:
Originally Posted by rnkayab
> The one possible flaw in your use of shared variables is that there is a possibility > of your while loops executing one extra time, due to non-atomic fetch & test of the > shared variable
How? What is a scenario that would do that?
At the lowest level, there are multiple instructions that execute to fetch the variable from memory, and perform the comparison. Typically, the variable is loaded into a register using a fetch from memory (probably one instruction), and then an arithmetic comparison is made against either another register or a constant value (another instruction). Finally, a branch instruction (jump or call) is executed depending on the state of bits in a flag register (another instruction). An interrupt may occur after any one of thes instructions, and modify the (in-memory) contents of the already-fetched variable. This sequence of instructions will vary somewhat depending upon the compiler/language, machine architecture, and compiler optimizations used. I don't think there is any way of guaranteeing that the fetch & test are performed atomically using any high level languages like C/C++.

--- rod.
 
Old 08-24-2006, 03:07 PM   #7
rnkayab
LQ Newbie
 
Registered: Aug 2006
Posts: 5

Original Poster
Rep: Reputation: 0
Thanks, rod, for this discussion.

Quote:
This cannot be possible. A concept central to correct interrupt processing is that it happens invisibly to all processes that are not part of the interrupt handler's private code and data space. How can an interrupt modify your private variable
So you mean that if my example code were a device driver, the interrupt routines can modify the private variable because they would run in the context of the process the driver would also run in?

Otherwise, interrupts shouldn't/can't directly (unless they reference memory incorrectly and write to the variable?).


Quote:
At the lowest level, there are multiple instructions that execute to fetch the variable from memory, and perform the comparison ... An interrupt may occur after any one of these instructions, and modify the (in-memory) contents of the already-fetched variable... I don't think there is any way of guaranteeing that the fetch & test are performed atomically using any high level languages like C/C++.
Atomic fetch & test might not be important here. We know that Isr1() cannot be preempted by a lower-level handler, so highestLevel is at least 1. If Isr1() is not interrupted, the behavior is correct. Otherwise, Isr1() is interrupted. If it is interrupted in the body of the while, then highestLevel will be at least 2, so I will restart correctly. Otherwise, Isr1() is interrupted in the condition of the while. Then even if Isr1() does not use the latest value (that is, highestLevel is still 1), this is OK - The loop won't restart which is OK as I know that I did my work in the body without being interrupted. Am I correct?

Code:
BOOLEAN Isr1 ()
{
  do {
    highestLevel = 1;
    // my work here
    counter++; 
    // more of my work here 
  } while ( highestLevel > 1 );  /* if true, Isr1 was interrupted */

  return TRUE;
}
 
Old 08-24-2006, 05:28 PM   #8
theNbomr
LQ 5k Club
 
Registered: Aug 2005
Distribution: OpenSuse, Fedora, Redhat, Debian
Posts: 5,399
Blog Entries: 2

Rep: Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908
Quote:
Originally Posted by rnkayab
So you mean that if my example code were a device driver, the interrupt routines can modify the private variable because they would run in the context of the process the driver would also run in?
Otherwise, interrupts shouldn't/can't directly (unless they reference memory incorrectly and write to the variable?).
Sounds correct to me.
Quote:
Originally Posted by rnkayab
Atomic fetch & test might not be important here. We know that Isr1() cannot be preempted by a lower-level handler, so highestLevel is at least 1. If Isr1() is not interrupted, the behavior is correct. Otherwise, Isr1() is interrupted. If it is interrupted in the body of the while, then highestLevel will be at least 2, so I will restart correctly. Otherwise, Isr1() is interrupted in the condition of the while. Then even if Isr1() does not use the latest value (that is, highestLevel is still 1), this is OK - The loop won't restart which is OK as I know that I did my work in the body without being interrupted. Am I correct?
This also sounds correct, according to your logic.

Now, you have me wondering what you are up to, and why you are concerned about detecting interrupts. It sounds as if you have a code section which is for some reason, sensitive to time during it's execution. If this is the case, you may be using the wrong tool for the job. You cannot rely on anything to not be interrupted for any amount of time in a non realtime OS. Your process may be time-sliced out of execution for arbitrary amounts of time, and an arbitrary number of times during any particular code section. Are you sure you shouldn't be using a real-time OS?

BTW, it is less confusing and more conventional to use the term low/high priority, rather than low/high level. At least, I hope I've been properly making the mental conversion between the two terms as you use them.

--- rod.
 
Old 08-25-2006, 02:00 PM   #9
rnkayab
LQ Newbie
 
Registered: Aug 2006
Posts: 5

Original Poster
Rep: Reputation: 0
Quote:
Now, you have me wondering what you are up to, and why you are concerned about detecting interrupts. It sounds as if you have a code section which is for some reason, sensitive to time during it's execution. If this is the case, you may be using the wrong tool for the job.
I am interested in real-time reactive programming for embedded controllers characterized by mapping anticipated input events to programmed reactions. A reaction may emit output events and up-date the internal state variables of the controller. For my C event handlers, I am interested in guaranteeing time and space boundedness, atomic execution, etc. I am currently implementing prioritized events where I am using a transactional approach to atomicity (such as in http://www.cs.washington.edu/homes/d...mcaml_icfp.pdf)

Quote:
You cannot rely on anything to not be interrupted for any amount of time in a non realtime OS. Your process may be time-sliced out of execution for arbitrary amounts of time, and an arbitrary number of times during any particular code section. Are you sure you shouldn't be using a real-time OS?
I am looking to write a device driver in Windows for data acquisition that "looks like" a controller. I am relying on using a higher priority event handlers (higher than normal Windows kernel and user code runs at).
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
How to know if I am using mbox-style or maildir-style? Niceman2005 Linux - General 1 09-23-2005 12:08 PM
Debugging ISR eshwar_ind Programming 1 06-18-2005 04:31 PM
VIM-style wrapping to OpenOffice style schmmd Linux - Software 1 12-21-2004 06:50 PM
how to change mandrake style menu to kde style menu msalimane Mandriva 1 12-07-2004 01:16 PM
Pentium Timer ISR obashir Programming 0 04-27-2003 04:45 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 12:02 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration