Skip to contentSkip to conversation

shirq locking rework

RFC PATCH In review Jan Kiszka 3 participants 14 messages Jun 19, 2007 – Jun 22, 2007 1 message not in the archive

lore
Share & tools
lore

Jan Kiszka

author
#0 lore raw

RFC PATCH 1/1 shirq locking rework

Hi Dmitry,

I had a look at the shared IRQ thing /wrt to my idea of switching between two list revisions. It turned out to be as nice as I expected when marvelling at the new xnintr_edge_shirq_handler() -- but it became horribly complex for setup/cleanup :(. So forget it, also the ring idea which took quite some part in the complexity increase.

Looking at xnintr_shirq_lock again: We have an atomic op, we have a memory barrier -- what do we actually gain by not using a usual xnlock here? I mean one that is per-IRQ, embedded in xnintr_shirq_t?

Well, I hate nested locks when it comes to real-time, but in this case I really found no simpler approach. There is the risk of deadlocks via

IRQ: xnintr_shirq::lock -> handler -> nklock vs.
Application: nklock -> xnintr_attach/detach -> xnintr_shirq::lock,

but we have no such case in-tree, nor should anyone try to do so elsewhere I assume (restriction is documented now). Note that this code is only compile-tested, and my brain still suffers from serious knots due to the first approach, thus no guarantee that I didn't messed things up.

Comments?

Jan

---
 ksrc/nucleus/intr.c |   70 +++++++++++++++-------------------------------------
 1 file changed, 21 insertions(+), 49 deletions(-)
ksrc/nucleus/intr.c +21 -49
Index: xenomai/ksrc/nucleus/intr.c
===================================================================
@@ -169,40 +169,12 @@ typedef struct xnintr_shirq {
 
 	xnintr_t *handlers;
 	int unhandled;
-#ifdef CONFIG_SMP
-	atomic_counter_t active;
-#endif
+	DECLARE_XNLOCK(lock);
 
 } xnintr_shirq_t;
 
 static xnintr_shirq_t xnshirqs[RTHAL_NR_IRQS];
 
-static inline void xnintr_shirq_lock(xnintr_shirq_t *shirq)
-{
-#ifdef CONFIG_SMP
-	xnarch_atomic_inc(&shirq->active);
-	xnarch_memory_barrier();
-#endif
-}
-
-static inline void xnintr_shirq_unlock(xnintr_shirq_t *shirq)
-{
-#ifdef CONFIG_SMP
-	xnarch_memory_barrier();
-	xnarch_atomic_dec(&shirq->active);
-#endif
-}
-
-void xnintr_synchronize(xnintr_t *intr)
-{
-#ifdef CONFIG_SMP
-	xnintr_shirq_t *shirq = &xnshirqs[intr->irq];
-
-	while (xnarch_atomic_get(&shirq->active))
-		cpu_relax();
-#endif
-}
-
 #if defined(CONFIG_XENO_OPT_SHIRQ_LEVEL)
 /*
  * Low-level interrupt handler dispatching the user-defined ISRs for
@@ -226,7 +198,7 @@ static void xnintr_shirq_handler(unsigne
 
 	++sched->inesting;
 
-	xnintr_shirq_lock(shirq);
+	xnlock_get(&shirq->lock);
 	intr = shirq->handlers;
 
 	while (intr) {
@@ -247,7 +219,7 @@ static void xnintr_shirq_handler(unsigne
 		intr = intr->next;
 	}
 
-	xnintr_shirq_unlock(shirq);
+	xnlock_put(&shirq->lock);
 
 	if (unlikely(s == XN_ISR_NONE)) {
 		if (++shirq->unhandled == XNINTR_MAX_UNHANDLED) {
@@ -297,7 +269,7 @@ static void xnintr_edge_shirq_handler(un
 
 	++sched->inesting;
 
-	xnintr_shirq_lock(shirq);
+	xnlock_get(&shirq->lock);
 	intr = shirq->handlers;
 
 	while (intr != end) {
@@ -328,7 +300,7 @@ static void xnintr_edge_shirq_handler(un
 			intr = shirq->handlers;
 	}
 
-	xnintr_shirq_unlock(shirq);
+	xnlock_put(&shirq->lock);
 
 	if (counter > MAX_EDGEIRQ_COUNTER)
 		xnlogerr
@@ -411,9 +383,12 @@ static inline int xnintr_irq_attach(xnin
 
 	__setbits(intr->flags, XN_ISR_ATTACHED);
 
-	/* Add a given interrupt object. */
 	intr->next = NULL;
+
+	/* Add a given interrupt object. */
+	xnlock_get(&shirq->lock);
 	*p = intr;
+	xnlock_put(&shirq->lock);
 
 	return 0;
 }
@@ -434,8 +409,10 @@ static inline int xnintr_irq_detach(xnin
 
 	while ((e = *p) != NULL) {
 		if (e == intr) {
-			/* Remove a given interrupt object from the list. */
+			/* Remove the given interrupt object from the list. */
+			xnlock_get(&shirq->lock);
 			*p = e->next;
+			xnlock_put(&shirq->lock);
 
 			/* Release the IRQ line if this was the last user */
 			if (shirq->handlers == NULL)
@@ -454,12 +431,8 @@ static inline int xnintr_irq_detach(xnin
 int xnintr_mount(void)
 {
 	int i;
-	for (i = 0; i < RTHAL_NR_IRQS; ++i) {
-		xnshirqs[i].handlers = NULL;
-#ifdef CONFIG_SMP
-		xnarch_atomic_set(&xnshirqs[i].active, 0);
-#endif
-	}
+	for (i = 0; i < RTHAL_NR_IRQS; ++i)
+		xnlock_init(&xnshirqs[i].lock);
 	return 0;
 }
 
@@ -475,7 +448,6 @@ static inline int xnintr_irq_detach(xnin
 	return xnarch_release_irq(intr->irq);
 }
 
-void xnintr_synchronize(xnintr_t *intr) {}
 int xnintr_mount(void) { return 0; }
 
 #endif /* CONFIG_XENO_OPT_SHIRQ_LEVEL || CONFIG_XENO_OPT_SHIRQ_EDGE */
@@ -651,6 +623,9 @@ int xnintr_destroy(xnintr_t *intr)
  * a low-level error occurred while attaching the interrupt. -EBUSY is
  * specifically returned if the interrupt object was already attached.
  *
+ * @note The caller <b>must not</b> hold nklock when invoking this service,
+ * this would cause deadlocks.
+ *
  * Environments:
  *
  * This service can be called from:
@@ -682,7 +657,7 @@ int xnintr_attach(xnintr_t *intr, void *
 
 	if (!err)
 		xnintr_stat_counter_inc();
-	
+
 	xnlock_put_irqrestore(&intrlock, s);
 
 	return err;
@@ -706,6 +681,9 @@ int xnintr_attach(xnintr_t *intr, void *
  * a non-attached interrupt object leads to a null-effect and returns
  * 0.
  *
+ * @note The caller <b>must not</b> hold nklock when invoking this service,
+ * this would cause deadlocks.
+ *
  * Environments:
  *
  * This service can be called from:
@@ -731,12 +709,6 @@ int xnintr_detach(xnintr_t *intr)
 
 	xnlock_put_irqrestore(&intrlock, s);
 
-	/* The idea here is to keep a detached interrupt object valid as long
-	   as the corresponding irq handler is running. This is one of the
-	   requirements to iterate over the xnintr_shirq_t::handlers list in
-	   xnintr_irq_handler() in a lockless way. */
-	xnintr_synchronize(intr);
-
 	return err;
 }
 
#1 lore raw

Hello Jan,

Quoted 5 lines

Well, I hate nested locks when it comes to real-time, but in this case I really found no simpler approach. There is the risk of deadlocks via

IRQ:            xnintr_shirq::lock -> handler -> nklock vs.
Application:    nklock -> xnintr_attach/detach -> xnintr_shirq::lock,

it's also relevant for the current code - xnintr_attach/detach() must not be called while holding the 'nklock'.

I think, your approach should work as well.. provided we have only a single reader vs. a single writter contention case, which seems to be the case here ('intrlock' is responsible for synchronization between xnintr_attach/detach()).. your approach does increase a worst case length of the lock(&intrlock) --> unlock(&intrlock) section... but that's unlikely to be critical.

I think, the patch I sent before addresses a reported earlier case with xnintr_edge_shirq_handler().. but your approach does make code shorter (and likely simpler), right? I don't see any problems it would possibly cause (maybe I'm missing smth yet :)

Signature · 2 lines
Best regards,
Dmitry Adamushko
author
#2 lore raw
Quoted 10 lines · Dmitry Adamushko

Hello Jan,

Quoted 5 lines

Well, I hate nested locks when it comes to real-time, but in this case I really found no simpler approach. There is the risk of deadlocks via

IRQ:            xnintr_shirq::lock -> handler -> nklock vs.
Application:    nklock -> xnintr_attach/detach -> xnintr_shirq::lock,

it's also relevant for the current code - xnintr_attach/detach() must not be called while holding the 'nklock'.

That's good, no new restriction (the existing one will be documented now).

Quoted 4 lines

I think, your approach should work as well.. provided we have only a single reader vs. a single writter contention case, which seems to be the case here ('intrlock' is responsible for synchronization between

Single writer is ensured by intrlock, single reader comes from the per-IRQ scope of the inner lock.

Quoted 8 lines

xnintr_attach/detach()).. your approach does increase a worst case length of the lock(&intrlock) --> unlock(&intrlock) section... but that's unlikely to be critical.

I think, the patch I sent before addresses a reported earlier case with xnintr_edge_shirq_handler().. but your approach does make code shorter (and likely simpler), right? I don't see any problems it would possibly cause (maybe I'm missing smth yet :)

I must confess I didn't get your idea immediately. Later on (cough, after hacking my own patch, cough) I had a closer look, and it first appeared fairly nice, despite the additional "ifs". But then I realised that "end == old_end" may produce false positives in case we have several times the same (and only the same) IRQ in a row. So, I'm afraid we have to live with only one candidate. :->

OK, will point our bug reporter to that patch now and ask for testing.

Jan

#3 lore raw
Quoted 10 lines · Jan Kiszka

[ ... ]

Quoted 8 lines

xnintr_attach/detach()).. your approach does increase a worst case length of the lock(&intrlock) --> unlock(&intrlock) section... but that's unlikely to be critical.

I think, the patch I sent before addresses a reported earlier case with xnintr_edge_shirq_handler().. but your approach does make code shorter (and likely simpler), right? I don't see any problems it would possibly cause (maybe I'm missing smth yet :)

Quoted 7 lines

I must confess I didn't get your idea immediately. Later on (cough, after hacking my own patch, cough) I had a closer look, and it first appeared fairly nice, despite the additional "ifs". But then I realised that "end == old_end" may produce false positives in case we have several times the same (and only the same) IRQ in a row.
So, I'm afraid
we have to live with only one candidate. :->

It's not clear, can you elaborate your (non-working) scenario in more details? :-)

Note: I sent the second patch with the following correction :

...

               } else if (code == XN_ISR_NONE && end == NULL) {
                       end = intr;
+                        old_end = NULL;
               }

...

I don't see why this idea can't work (even if I made yet another error). Under some circumstances the following code will be triggered to end a loop even when 'end' is still valid

                       if (end && old_end == end)
                               intr = NULL;

_but_ it'd be exactly a case when "intr == end" and hence, the loop would be finished anyway by the "while (intr && intr != end)" condition.. So sometimes it acts as yet _another_ check to exit the loop, IOW "while (intr && intr != end)" may be converted to just "while (intr)".

Quoted 3 lines

Jan

Signature · 2 lines
Best regards,
Dmitry Adamushko
author
#4 lore raw
Quoted 35 lines · Dmitry Adamushko
Quoted 10 lines · Jan Kiszka

[ ... ]

Quoted 8 lines

xnintr_attach/detach()).. your approach does increase a worst case length of the lock(&intrlock) --> unlock(&intrlock) section... but that's unlikely to be critical.

I think, the patch I sent before addresses a reported earlier case with xnintr_edge_shirq_handler().. but your approach does make code shorter (and likely simpler), right? I don't see any problems it would possibly cause (maybe I'm missing smth yet :)

Quoted 7 lines

I must confess I didn't get your idea immediately. Later on (cough, after hacking my own patch, cough) I had a closer look, and it first appeared fairly nice, despite the additional "ifs". But then I realised that "end == old_end" may produce false positives in case we have several times the same (and only the same) IRQ in a row.
So, I'm afraid
we have to live with only one candidate. :->

It's not clear, can you elaborate your (non-working) scenario in more details? :-)

Note: I sent the second patch with the following correction :

...

               } else if (code == XN_ISR_NONE && end == NULL) {
                       end = intr;
+                        old_end = NULL;
               }

...

I don't see why this idea can't work (even if I made yet another error). Under some circumstances the following code will be triggered to end a loop even when 'end' is still valid

Quoted 8 lines
                       if (end && old_end == end)
                               intr = NULL;

_but_ it'd be exactly a case when "intr == end" and hence, the loop would be finished anyway by the "while (intr && intr != end)" condition.. So sometimes it acts as yet _another_ check to exit the loop, IOW "while (intr && intr != end)" may be converted to just "while (intr)".

Yeah, looks like you are right again, should work as well.

Unfortunately, that whole thing make me meditate about the whole issue again. And now I wonder why we make such a fuss about locking for shared IRQs (where it should be correct with any of the new patches) while we do not care about the non-shared, standard scenario. I currently find no reason why we shouldn't race when some non-shared IRQ pops up on one CPU and deregistration takes place on another. Also in this case, the xnintr object must remain valid for the whole handler execution time. Do we need

struct xnintr_irq {
	xnintr_t *handler;
	<whatever-lock>;
} xnirqs[RTHAL_NR_IRQS];

unconditionally? Or should we better move the whole locking thing into i-pipe somehow? Argh.

Well, and I wonder what this xnarch_memory_barrier() at each handler entry is for. Seems to be there for ages, don't see why right now. (The kernel has a golden rule for this: no barrier without comments!)

Sigh, the never ending IRQ story...

Jan

#5 lore raw
Quoted 6 lines · Jan Kiszka

[ ... ]

Unfortunately, that whole thing make me meditate about the whole issue again. And now I wonder why we make such a fuss about locking for shared IRQs (where it should be correct with any of the new patches) while we do not care about the non-shared, standard scenario.

surprise-surprise.. sure, one way or another it must be fixed. heh.. too many bugs (and I don't even want to say who's responsible) :-/

Quoted 1 line

Sigh, the never ending IRQ story...

should be reviewed.

Quoted 3 lines

Jan

Signature · 2 lines
Best regards,
Dmitry Adamushko
author
#6 lore raw
Quoted 10 lines · Dmitry Adamushko
Quoted 6 lines · Jan Kiszka

[ ... ]

Unfortunately, that whole thing make me meditate about the whole issue again. And now I wonder why we make such a fuss about locking for shared IRQs (where it should be correct with any of the new patches) while we do not care about the non-shared, standard scenario.

surprise-surprise.. sure, one way or another it must be fixed. heh.. too many bugs (and I don't even want to say who's responsible) :-/

I wouldn't accept all the responsibility if I were you. We all went through this code several times. It's a sign that the design might be too complex now, and I feel like having some share in this.

Quoted 5 lines
Quoted 2 lines

Sigh, the never ending IRQ story...

should be reviewed.

Things get worse, at least with XENO_OPT_STATS: Due to the runtime statistics collection, we may end up with dangling references to our xnintr object even after xnintr_shirq_unlock().

We would actually need some kind of IRQ_INPROGRESS + synchronize_irq() at i-pipe level. But we have the problem, in contrast to the kernel, that we reschedule from inside the handler (more precisely: at nucleus level), thus synchronize_irq() would not just wait on some simple handler to exit...

Jan

#7 lore raw
Quoted 5 lines · Jan Kiszka
Quoted 3 lines

[ .. ]
surprise-surprise.. sure, one way or another it must be fixed. heh.. too many bugs (and I don't even want to say who's responsible) :-/

I wouldn't accept all the responsibility if I were you.

I have no problems in this respect. I was just a bit sarcastic with the way to say "it's my fault".

Quoted 2 lines

It's a sign that the design might be
too complex now

frankly speaking, I don't think it's really complex :)

Quoted 9 lines

Things get worse, at least with XENO_OPT_STATS: Due to the runtime statistics collection, we may end up with dangling references to our xnintr object even after xnintr_shirq_unlock().

We would actually need some kind of IRQ_INPROGRESS + synchronize_irq() at i-pipe level. But we have the problem, in contrast to the kernel, that we reschedule from inside the handler (more precisely: at nucleus level), thus synchronize_irq() would not just wait on some simple handler to exit...

Yeah.. we had already conversations on this topic (I think with Philippe) and, if I recall right, that was one of the reasons. That's why synchronize_irq() is in the nucleus layer.

Quoted 3 lines

Jan

Signature · 2 lines
Best regards,
Dmitry Adamushko
author
#8 lore raw
Quoted 27 lines · Dmitry Adamushko
Quoted 5 lines · Jan Kiszka
Quoted 3 lines

[ .. ]
surprise-surprise.. sure, one way or another it must be fixed. heh.. too many bugs (and I don't even want to say who's responsible) :-/

I wouldn't accept all the responsibility if I were you.

I have no problems in this respect. I was just a bit sarcastic with the way to say "it's my fault".

Quoted 2 lines

It's a sign that the design might be
too complex now

frankly speaking, I don't think it's really complex :)

Quoted 9 lines

Things get worse, at least with XENO_OPT_STATS: Due to the runtime statistics collection, we may end up with dangling references to our xnintr object even after xnintr_shirq_unlock().

We would actually need some kind of IRQ_INPROGRESS + synchronize_irq() at i-pipe level. But we have the problem, in contrast to the kernel, that we reschedule from inside the handler (more precisely: at nucleus level), thus synchronize_irq() would not just wait on some simple handler to exit...

Quoted 3 lines

Yeah.. we had already conversations on this topic (I think with Philippe) and, if I recall right, that was one of the reasons. That's why synchronize_irq() is in the nucleus layer.

Hmm, then we may be forced to get the cookie out of ipipe's hands again and put it back into a nucleus irq array, i.e. move the cookie dereferencing under a nucleus managed per-irq lock. But there is still the issue with xnsched_t::current_account...

author
#9 lore raw
Quoted 24 lines · Jan Kiszka
Quoted 23 lines · Dmitry Adamushko
Quoted 4 lines · Jan Kiszka
Quoted 3 lines

[ .. ]
surprise-surprise.. sure, one way or another it must be fixed. heh.. too many bugs (and I don't even want to say who's responsible) :-/

I wouldn't accept all the responsibility if I were you.

I have no problems in this respect. I was just a bit sarcastic with the way to say "it's my fault".

Quoted 2 lines

It's a sign that the design might be
too complex now

frankly speaking, I don't think it's really complex :)

Quoted 9 lines

Things get worse, at least with XENO_OPT_STATS: Due to the runtime statistics collection, we may end up with dangling references to our xnintr object even after xnintr_shirq_unlock().

We would actually need some kind of IRQ_INPROGRESS + synchronize_irq() at i-pipe level. But we have the problem, in contrast to the kernel, that we reschedule from inside the handler (more precisely: at nucleus level), thus synchronize_irq() would not just wait on some simple handler to exit...

Quoted 9 lines
Quoted 3 lines

Yeah.. we had already conversations on this topic (I think with Philippe) and, if I recall right, that was one of the reasons. That's why synchronize_irq() is in the nucleus layer.

Hmm, then we may be forced to get the cookie out of ipipe's hands again and put it back into a nucleus irq array, i.e. move the cookie dereferencing under a nucleus managed per-irq lock. But there is still the issue with xnsched_t::current_account...

Hell, what a mess...

Here is a first attempt to address the remaining issues. Take it with a huge grain of salt, I haven't yet made up my mind if it really solves our races and if it isn't too ugly.

Please direct special attention to

- xnintr_sync_stat_references (the comment says it all)

- xnintr_edge_shirq_handler (unrelated change, but I somehow felt like this is nicer)

Only compile-tested under various .configs. Any comment welcome.

Thanks,
Jan

ksrc/nucleus/intr.c +70 -36
Index: xenomai/ksrc/nucleus/intr.c
===================================================================
@@ -41,6 +41,18 @@
 
 DEFINE_PRIVATE_XNLOCK(intrlock);
 
+typedef struct xnintr_irq {
+
+	DECLARE_XNLOCK(lock);
+
+#if defined(CONFIG_XENO_OPT_SHIRQ_LEVEL) || defined(CONFIG_XENO_OPT_SHIRQ_EDGE)
+	xnintr_t *handlers;
+	int unhandled;
+#endif
+} ____cacheline_aligned_in_smp xnintr_irq_t;
+
+static xnintr_irq_t xnirqs[RTHAL_NR_IRQS];
+
 #ifdef CONFIG_XENO_OPT_STATS
 xnintr_t nkclock;	/* Only for statistics */
 int xnintr_count = 1;	/* Number of attached xnintr objects + nkclock */
@@ -63,9 +75,23 @@ static inline void xnintr_stat_counter_d
 	xnarch_memory_barrier();
 	xnintr_list_rev++;
 }
+
+static inline void xnintr_sync_stat_references(xnintr_t *intr)
+{
+	int cpu;
+
+	for_each_online_cpu(cpu) {
+		xnsched_t *sched = xnpod_sched_slot(cpu);
+
+		/* I don't feel very well... hacking this. */
+		while (sched->current_account == &intr->stat[cpu].account)
+			cpu_relax();
+	}
+}
 #else
 static inline void xnintr_stat_counter_inc(void) {}
 static inline void xnintr_stat_counter_dec(void) {}
+static inline void xnintr_sync_stat_references(xnintr_t *intr) {}
 #endif /* CONFIG_XENO_OPT_STATS */
 
 /*
@@ -76,7 +102,7 @@ static inline void xnintr_stat_counter_d
 static void xnintr_irq_handler(unsigned irq, void *cookie)
 {
 	xnsched_t *sched = xnpod_current_sched();
-	xnintr_t *intr = (xnintr_t *)cookie;
+	xnintr_t *intr;
 	xnstat_runtime_t *prev;
 	xnticks_t start;
 	int s;
@@ -86,6 +112,16 @@ static void xnintr_irq_handler(unsigned 
 	xnltt_log_event(xeno_ev_ienter, irq);
 
 	++sched->inesting;
+
+	xnlock_get(&xnirqs[irq].lock);
+
+#ifdef CONFIG_SMP
+	/* In SMP case, we have to reload the cookie under the per-IRQ lock
+	   to avoid racing with xnintr_detach. */
+	intr = rthal_irq_cookie(&rthal_domain, irq);
+#else
+	intr = cookie;
+#endif
 	s = intr->isr(intr);
 
 	if (unlikely(s == XN_ISR_NONE)) {
@@ -102,6 +138,8 @@ static void xnintr_irq_handler(unsigned 
 		intr->unhandled = 0;
 	}
 
+	xnlock_put(&xnirqs[irq].lock);
+
 	if (s & XN_ISR_PROPAGATE)
 		xnarch_chain_irq(irq);
 	else if (!(s & XN_ISR_NOENABLE))
@@ -161,18 +199,6 @@ void xnintr_clock_handler(void)
 
 #if defined(CONFIG_XENO_OPT_SHIRQ_LEVEL) || defined(CONFIG_XENO_OPT_SHIRQ_EDGE)
 
-typedef struct xnintr_shirq {
-
-	xnintr_t *handlers;
-	int unhandled;
-#ifdef CONFIG_SMP
-	xnlock_t lock;
-#endif
-
-} xnintr_shirq_t;
-
-static xnintr_shirq_t xnshirqs[RTHAL_NR_IRQS];
-
 #if defined(CONFIG_XENO_OPT_SHIRQ_LEVEL)
 /*
  * Low-level interrupt handler dispatching the user-defined ISRs for
@@ -184,7 +210,7 @@ static void xnintr_shirq_handler(unsigne
 	xnsched_t *sched = xnpod_current_sched();
 	xnstat_runtime_t *prev;
 	xnticks_t start;
-	xnintr_shirq_t *shirq = &xnshirqs[irq];
+	xnintr_irq_t *shirq = &xnirqs[irq];
 	xnintr_t *intr;
 	int s = 0;
 
@@ -253,7 +279,7 @@ static void xnintr_edge_shirq_handler(un
 	xnsched_t *sched = xnpod_current_sched();
 	xnstat_runtime_t *prev;
 	xnticks_t start;
-	xnintr_shirq_t *shirq = &xnshirqs[irq];
+	xnintr_irq_t *shirq = &xnirqs[irq];
 	xnintr_t *intr, *end = NULL;
 	int s = 0, counter = 0;
 
@@ -277,15 +303,15 @@ static void xnintr_edge_shirq_handler(un
 		s |= ret;
 
 		if (code == XN_ISR_HANDLED) {
-			end = NULL;
+			if (!(end = (intr->next)))
+				end = shirq->handlers;
 			xnstat_counter_inc(
 				&intr->stat[xnsched_cpu(sched)].hits);
 			xnstat_runtime_lazy_switch(sched,
 				&intr->stat[xnsched_cpu(sched)].account,
 				start);
 			start = xnstat_runtime_now();
-		} else if (code == XN_ISR_NONE && end == NULL)
-			end = intr;
+		}
 
 		if (counter++ > MAX_EDGEIRQ_COUNTER)
 			break;
@@ -326,7 +352,7 @@ static void xnintr_edge_shirq_handler(un
 
 static inline int xnintr_irq_attach(xnintr_t *intr)
 {
-	xnintr_shirq_t *shirq = &xnshirqs[intr->irq];
+	xnintr_irq_t *shirq = &xnirqs[intr->irq];
 	xnintr_t *prev, **p = &shirq->handlers;
 	int err;
 
@@ -379,17 +405,16 @@ static inline int xnintr_irq_attach(xnin
 
 	intr->next = NULL;
 
-	/* Add a given interrupt object. */
-	xnlock_get(&shirq->lock);
+	/* Add the given interrupt object. No need to synchronise with the IRQ
+	   handler, we are only extending the chain. */
 	*p = intr;
-	xnlock_put(&shirq->lock);
 
 	return 0;
 }
 
 static inline int xnintr_irq_detach(xnintr_t *intr)
 {
-	xnintr_shirq_t *shirq = &xnshirqs[intr->irq];
+	xnintr_irq_t *shirq = &xnirqs[intr->irq];
 	xnintr_t *e, **p = &shirq->handlers;
 	int err = 0;
 
@@ -406,6 +431,7 @@ static inline int xnintr_irq_detach(xnin
 			/* Remove the given interrupt object from the list. */
 			xnlock_get(&shirq->lock);
 			*p = e->next;
+			xnintr_sync_stat_references(intr);
 			xnlock_put(&shirq->lock);
 
 			/* Release the IRQ line if this was the last user */
@@ -422,14 +448,6 @@ static inline int xnintr_irq_detach(xnin
 	return err;
 }
 
-int xnintr_mount(void)
-{
-	int i;
-	for (i = 0; i < RTHAL_NR_IRQS; ++i)
-		xnlock_init(&xnshirqs[i].lock);
-	return 0;
-}
-
 #else /* !CONFIG_XENO_OPT_SHIRQ_LEVEL && !CONFIG_XENO_OPT_SHIRQ_EDGE */
 
 static inline int xnintr_irq_attach(xnintr_t *intr)
@@ -439,13 +457,29 @@ static inline int xnintr_irq_attach(xnin
 
 static inline int xnintr_irq_detach(xnintr_t *intr)
 {
-	return xnarch_release_irq(intr->irq);
-}
+	int irq = intr->irq;
+	int err;
+
+	xnlock_get(&xnirqs[irq].lock);
 
-int xnintr_mount(void) { return 0; }
+	err = xnarch_release_irq(intr->irq);
+	xnintr_sync_stat_references(intr);
+
+	xnlock_put(&xnirqs[irq].lock);
+
+	return err;
+}
 
 #endif /* CONFIG_XENO_OPT_SHIRQ_LEVEL || CONFIG_XENO_OPT_SHIRQ_EDGE */
 
+int xnintr_mount(void)
+{
+	int i;
+	for (i = 0; i < RTHAL_NR_IRQS; ++i)
+		xnlock_init(&xnirqs[i].lock);
+	return 0;
+}
+
 /*!
  * \fn int xnintr_init (xnintr_t *intr,const char *name,unsigned irq,xnisr_t isr,xniack_t iack,xnflags_t flags)
  * \brief Initialize an interrupt object.
@@ -809,7 +843,7 @@ int xnintr_irq_proc(unsigned int irq, ch
 	xnlock_get_irqsave(&intrlock, s);
 
 #if defined(CONFIG_XENO_OPT_SHIRQ_LEVEL) || defined(CONFIG_XENO_OPT_SHIRQ_EDGE)
-	intr = xnshirqs[irq].handlers;
+	intr = xnirqs[irq].handlers;
 	if (intr) {
 		strcpy(p, "        "); p += 8;
 
@@ -862,7 +896,7 @@ int xnintr_query(int irq, int *cpu, xnin
 	else if (irq == XNARCH_TIMER_IRQ)
 		intr = &nkclock;
 	else
-		intr = xnshirqs[irq].handlers;
+		intr = xnirqs[irq].handlers;
 #else /* !CONFIG_XENO_OPT_SHIRQ_LEVEL && !CONFIG_XENO_OPT_SHIRQ_EDGE */
 	if (*prev)
 		intr = NULL;
#10 lore raw
Quoted 19 lines · Jan Kiszka

[ ... ]

Only compile-tested under various .configs. Any comment welcome.

ksrc/nucleus/intr.c +2 -1
@@ -76,7 +102,7 @@ static inline void xnintr_stat_counter_d
 static void xnintr_irq_handler(unsigned irq, void *cookie)
 {
        xnsched_t *sched = xnpod_current_sched();
-       xnintr_t *intr = (xnintr_t *)cookie;
+       xnintr_t *intr;
        xnstat_runtime_t *prev;
        xnticks_t start;
        int s;
@@ -86,6 +112,16 @@ static void xnintr_irq_handler(unsigned
        xnltt_log_event(xeno_ev_ienter, irq);
 
        ++sched->inesting;
+

ksrc/nucleus/intr.c static void xnintr_irq_handler(unsigned · line 125

Quoted 10 lines
ksrc/nucleus/intr.c +9 -0
+       xnlock_get(&xnirqs[irq].lock);
+
+#ifdef CONFIG_SMP
+       /* In SMP case, we have to reload the cookie under the per-IRQ lock
+          to avoid racing with xnintr_detach. */
+       intr = rthal_irq_cookie(&rthal_domain, irq);
+#else
+       intr = cookie;
+#endif
        s = intr->isr(intr);

I guess, 'intr' can be NULL here.

Could you please send me attached (non-inlined) a combo patch on top of the trunk version (as I see this one seems to be on top of your previous one)? I'll try to come up with some solution during this weekend.

Signature · 2 lines
Best regards,
Dmitry Adamushko
author
#11 lore raw
Quoted 18 lines · Dmitry Adamushko
Quoted 17 lines · Jan Kiszka

[ ... ]

Only compile-tested under various .configs. Any comment welcome.

ksrc/nucleus/intr.c +1 -1
@@ -76,7 +102,7 @@ static inline void xnintr_stat_counter_d
 static void xnintr_irq_handler(unsigned irq, void *cookie)
 {
        xnsched_t *sched = xnpod_current_sched();
-       xnintr_t *intr = (xnintr_t *)cookie;
+       xnintr_t *intr;
        xnstat_runtime_t *prev;
        xnticks_t start;
        int s;
@@ -86,6 +112,16 @@ static void xnintr_irq_handler(unsigned
        xnltt_log_event(xeno_ev_ienter, irq);
 
Quoted 15 lines

ksrc/nucleus/intr.c static void xnintr_irq_handler(unsigned · line 125

Quoted 13 lines
ksrc/nucleus/intr.c +5 -0
        ++sched->inesting;
+
+       xnlock_get(&xnirqs[irq].lock);
+
+#ifdef CONFIG_SMP
+       /* In SMP case, we have to reload the cookie under the per-IRQ

lock

ksrc/nucleus/intr.c +5 -0
+          to avoid racing with xnintr_detach. */
+       intr = rthal_irq_cookie(&rthal_domain, irq);
+#else
+       intr = cookie;
+#endif
        s = intr->isr(intr);

I guess, 'intr' can be NULL here.

Yeah, needs to be caught as well.

Quoted 5 lines

Could you please send me attached (non-inlined) a combo patch on top of the trunk version (as I see this one seems to be on top of your previous one)? I'll try to come up with some solution during this weekend.

My patch was an attachment (though I wonder we everyone has so much problems with inlines - broken mailers all over the place), and it was already against trunk (#2654). Philippe merged my preliminary fix this morning.

Looking forward to your suggestions!

Jan

A message that is not in the archive (sent off-list or not yet received): [email protected]

#13 lore raw
Quoted 3 lines · Jan Kiszka
Quoted 2 lines · Jan Kiszka

Well, and I wonder what this xnarch_memory_barrier() at each handler entry is for. Seems to be there for ages, don't see why right now.

AFAICT, this probably dates back to Xenomai 1.x, when we used to have a threaded interrupt model. The actual code looked like as follows, and the barrier was likely here to make sure that any change to the interrupt hit counter was visible from any other CPU which would run the interrupt service thread. The funny thing is that we did not have SMP support at that time, anyway...

static void xnintr_irq_handler (unsigned irq, void *cookie)

{
    xnintr_t *intr = (xnintr_t *)cookie;
    int s = XN_ISR_SCHED_T;

    intr->hits++;

    xnarch_memory_barrier();

In short, I don't see any reason to keep this membar.

Quoted 2 lines

(The

Quoted 1 line

kernel has a golden rule for this: no barrier without comments!)

Yeah, right. It looks like the kernel has a slew of very official golden rules it basically does not care one dime to enforce in practice. Looking at the code, commenting membars is surely one of them...

Signature · 1 line
Philippe.
author
#14 lore raw
Quoted 23 lines · Philippe Gerum
Quoted 3 lines · Jan Kiszka
Quoted 2 lines · Jan Kiszka

Well, and I wonder what this xnarch_memory_barrier() at each handler entry is for. Seems to be there for ages, don't see why right now.

AFAICT, this probably dates back to Xenomai 1.x, when we used to have a threaded interrupt model. The actual code looked like as follows, and the barrier was likely here to make sure that any change to the interrupt hit counter was visible from any other CPU which would run the interrupt service thread. The funny thing is that we did not have SMP support at that time, anyway...

static void xnintr_irq_handler (unsigned irq, void *cookie)

{
    xnintr_t *intr = (xnintr_t *)cookie;
    int s = XN_ISR_SCHED_T;

    intr->hits++;

    xnarch_memory_barrier();
Quoted 1 line

In short, I don't see any reason to keep this membar.

Fascinating: So many people came along this place, but no one dared to touch it. :)

Quoted 8 lines
Quoted 3 lines

(The

Quoted 1 line

kernel has a golden rule for this: no barrier without comments!)

Yeah, right. It looks like the kernel has a slew of very official golden rules it basically does not care one dime to enforce in practice. Looking at the code, commenting membars is surely one of them...

I saw Andrew Morton being strictly after uncommented ones in new code at least.

Jan