From mboxrd@z Thu Jan 1 00:00:00 1970 Message-ID: <451914C5.10008@domain.hid> Date: Tue, 26 Sep 2006 13:53:41 +0200 From: Jan Kiszka MIME-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha1; protocol="application/pgp-signature"; boundary="------------enig8012160F8C8A1AFE097577D2" Sender: jan.kiszka@domain.hid Subject: [Xenomai-core] [PATCH] rework xnintr locking, enhance proc output List-Id: "Xenomai life and development \(bug reports, patches, discussions\)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: xenomai-core This is an OpenPGP/MIME signed message (RFC 2440 and 3156) --------------enig8012160F8C8A1AFE097577D2 Content-Type: multipart/mixed; boundary="------------010907050001060702080706" This is a multi-part message in MIME format. --------------010907050001060702080706 Content-Type: text/plain; charset=ISO-8859-15 Content-Transfer-Encoding: quoted-printable This patch series reworks the locking of xnintr_attach and xnintr_detach with respect to xnintr_irq_proc. So far the locking during proc output contains a risk of deadlock when an IRQ was detached concurrently. The new version also introduces IRQ names for the non-shared case so that drivers or other IRQ registering instances are always made visible. The series consists of the functional changes in the first patch (xnintr-locking-v4.patch) and a code reordering in the second one (xnintr-reorder-v2.patch). Apply in that order. Credits definitely go to Dmitry as well who helped a lot with cleaning up and reordering my first patch version. Jan PS: This series also paves the way for upcoming patches to add (almost) perfectly fair IRQ CPU load statistics. Stay tuned! 8) --------------010907050001060702080706 Content-Type: text/plain; name="xnintr-locking-v4.patch" Content-Transfer-Encoding: quoted-printable Content-Disposition: inline; filename="xnintr-locking-v4.patch" --- ChangeLog | 7 + ksrc/nucleus/intr.c | 201 +++++++++++++++++++++++++--------------------= ------- 2 files changed, 104 insertions(+), 104 deletions(-) Index: xenomai/ksrc/nucleus/intr.c =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- xenomai.orig/ksrc/nucleus/intr.c +++ xenomai/ksrc/nucleus/intr.c @@ -3,6 +3,7 @@ * \author Philippe Gerum * * Copyright (C) 2001,2002,2003 Philippe Gerum . + * Copyright (C) 2005,2006 Dmitry Adamushko = =2E * * Xenomai is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published @@ -35,19 +36,19 @@ #include #include =20 +#ifdef CONFIG_SMP +xnlock_t intrlock; +#endif /* CONFIG_SMP */ + xnintr_t nkclock; =20 static void xnintr_irq_handler(unsigned irq, void *cookie); =20 -#if defined(CONFIG_XENO_OPT_SHIRQ_LEVEL) || defined(CONFIG_XENO_OPT_SHIR= Q_EDGE) - -/* Helper functions. */ -static int xnintr_shirq_attach(xnintr_t *intr, void *cookie); -static int xnintr_shirq_detach(xnintr_t *intr); - -#endif /* CONFIG_XENO_OPT_SHIRQ_LEVEL || CONFIG_XENO_OPT_SHIRQ_EDGE */ +static int xnintr_irq_attach(xnintr_t *intr); +static int xnintr_irq_detach(xnintr_t *intr); +static void xnintr_synchronize(xnintr_t *intr); =20 -/*!=20 +/*! * \fn int xnintr_init (xnintr_t *intr,const char *name,unsigned irq,xni= sr_t isr,xniack_t iack,xnflags_t flags) * \brief Initialize an interrupt object. * @@ -198,7 +199,7 @@ int xnintr_destroy(xnintr_t *intr) return 0; } =20 -/*!=20 +/*! * \fn int xnintr_attach (xnintr_t *intr, void *cookie); * \brief Attach an interrupt object. * @@ -234,14 +235,19 @@ int xnintr_destroy(xnintr_t *intr) =20 int xnintr_attach(xnintr_t *intr, void *cookie) { + int err; + spl_t s; + intr->hits =3D 0; intr->cookie =3D cookie; -#if defined(CONFIG_XENO_OPT_SHIRQ_LEVEL) || defined(CONFIG_XENO_OPT_SHIR= Q_EDGE) - return xnintr_shirq_attach(intr, cookie); -#else /* !CONFIG_XENO_OPT_SHIRQ_LEVEL && !CONFIG_XENO_OPT_SHIRQ_EDGE */ - return xnarch_hook_irq(intr->irq, &xnintr_irq_handler, intr->iack, - intr); -#endif /* CONFIG_XENO_OPT_SHIRQ_LEVEL || CONFIG_XENO_OPT_SHIRQ_EDGE */ + + xnlock_get_irqsave(&intrlock, s); + + err =3D xnintr_irq_attach(intr); + + xnlock_put_irqrestore(&intrlock, s); + + return err; } =20 /*!=20 @@ -275,11 +281,22 @@ int xnintr_attach(xnintr_t *intr, void * =20 int xnintr_detach(xnintr_t *intr) { -#if defined(CONFIG_XENO_OPT_SHIRQ_LEVEL) || defined(CONFIG_XENO_OPT_SHIR= Q_EDGE) - return xnintr_shirq_detach(intr); -#else /* !CONFIG_XENO_OPT_SHIRQ_LEVEL && !CONFIG_XENO_OPT_SHIRQ_EDGE */ - return xnarch_release_irq(intr->irq); -#endif /* CONFIG_XENO_OPT_SHIRQ_LEVEL || CONFIG_XENO_OPT_SHIRQ_EDGE */ + int err; + spl_t s; + + xnlock_get_irqsave(&intrlock, s); + + err =3D xnintr_irq_detach(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; } =20 /*!=20 @@ -436,42 +453,37 @@ typedef struct xnintr_shirq { int unhandled; #ifdef CONFIG_SMP atomic_counter_t active; -#endif /* CONFIG_SMP */ +#endif =20 } xnintr_shirq_t; =20 static xnintr_shirq_t xnshirqs[RTHAL_NR_IRQS]; =20 -#ifdef CONFIG_SMP static inline void xnintr_shirq_lock(xnintr_shirq_t *shirq) { +#ifdef CONFIG_SMP xnarch_atomic_inc(&shirq->active); +#endif } =20 static inline void xnintr_shirq_unlock(xnintr_shirq_t *shirq) { +#ifdef CONFIG_SMP xnarch_atomic_dec(&shirq->active); +#endif } =20 -static inline void xnintr_shirq_spin(xnintr_shirq_t *shirq) +void xnintr_synchronize(xnintr_t *intr) { +#ifdef CONFIG_SMP + xnintr_shirq_t *shirq =3D &xnshirqs[intr->irq]; + while (xnarch_atomic_get(&shirq->active)) cpu_relax(); +#endif } -#else /* !CONFIG_SMP */ -static inline void xnintr_shirq_lock(xnintr_shirq_t *shirq) -{ -} -static inline void xnintr_shirq_unlock(xnintr_shirq_t *shirq) -{ -} -static inline void xnintr_shirq_spin(xnintr_shirq_t *shirq) -{ -} -#endif /* CONFIG_SMP */ =20 #if defined(CONFIG_XENO_OPT_SHIRQ_LEVEL) - /* * Low-level interrupt handler dispatching the user-defined ISRs for * shared interrupts -- Called with interrupts off. @@ -524,7 +536,6 @@ static void xnintr_shirq_handler(unsigne #endif /* CONFIG_XENO_OPT_SHIRQ_LEVEL */ =20 #if defined(CONFIG_XENO_OPT_SHIRQ_EDGE) - /* * Low-level interrupt handler dispatching the user-defined ISRs for * shared edge-triggered interrupts -- Called with interrupts off. @@ -597,32 +608,25 @@ static void xnintr_edge_shirq_handler(un =20 #endif /* CONFIG_XENO_OPT_SHIRQ_EDGE */ =20 -static int xnintr_shirq_attach(xnintr_t *intr, void *cookie) +int xnintr_irq_attach(xnintr_t *intr) { xnintr_shirq_t *shirq =3D &xnshirqs[intr->irq]; xnintr_t *prev, **p =3D &shirq->handlers; - int err =3D 0; - spl_t s; + int err; =20 if (intr->irq >=3D RTHAL_NR_IRQS) return -EINVAL; =20 - xnlock_get_irqsave(&nklock, s); - - if (__testbits(intr->flags, XN_ISR_ATTACHED)) { - err =3D -EPERM; - goto unlock_and_exit; - } + if (__testbits(intr->flags, XN_ISR_ATTACHED)) + return -EPERM; =20 if ((prev =3D *p) !=3D NULL) { /* Check on whether the shared mode is allowed. */ if (!(prev->flags & intr->flags & XN_ISR_SHARED) || (prev->iack !=3D intr->iack) || ((prev->flags & XN_ISR_EDGE) !=3D - (intr->flags & XN_ISR_EDGE))) { - err =3D -EBUSY; - goto unlock_and_exit; - } + (intr->flags & XN_ISR_EDGE))) + return -EBUSY; =20 /* Get a position at the end of the list to insert the new element. */= while (prev) { @@ -647,7 +651,7 @@ static int xnintr_shirq_attach(xnintr_t=20 =20 err =3D xnarch_hook_irq(intr->irq, handler, intr->iack, intr); if (err) - goto unlock_and_exit; + return err; } =20 __setbits(intr->flags, XN_ISR_ATTACHED); @@ -656,28 +660,20 @@ static int xnintr_shirq_attach(xnintr_t=20 intr->next =3D NULL; *p =3D intr; =20 - unlock_and_exit: - - xnlock_put_irqrestore(&nklock, s); - return err; + return 0; } =20 -int xnintr_shirq_detach(xnintr_t *intr) +int xnintr_irq_detach(xnintr_t *intr) { xnintr_shirq_t *shirq =3D &xnshirqs[intr->irq]; xnintr_t *e, **p =3D &shirq->handlers; int err =3D 0; - spl_t s; =20 if (intr->irq >=3D RTHAL_NR_IRQS) return -EINVAL; =20 - xnlock_get_irqsave(&nklock, s); - - if (!__testbits(intr->flags, XN_ISR_ATTACHED)) { - xnlock_put_irqrestore(&nklock, s); + if (!__testbits(intr->flags, XN_ISR_ATTACHED)) return -EPERM; - } =20 __clrbits(intr->flags, XN_ISR_ATTACHED); =20 @@ -690,23 +686,13 @@ int xnintr_shirq_detach(xnintr_t *intr) if (shirq->handlers =3D=3D NULL) err =3D xnarch_release_irq(intr->irq); =20 - xnlock_put_irqrestore(&nklock, 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 re= quirements - to iterate over the xnintr_shirq_t::handlers list in xnintr_irq_ha= ndler() - in a lockless way. */ - - xnintr_shirq_spin(shirq); return err; } p =3D &e->next; } =20 - xnlock_put_irqrestore(&nklock, s); - - xnlogerr - ("attempted to detach a non previously attached interrupt object.\n= "); + xnlogerr("attempted to detach a non previously attached interrupt " + "object.\n"); return err; } =20 @@ -717,16 +703,33 @@ int xnintr_mount(void) xnshirqs[i].handlers =3D NULL; #ifdef CONFIG_SMP atomic_set(&xnshirqs[i].active, 0); -#endif /* CONFIG_SMP */ +#endif } return 0; } =20 +#else /* !CONFIG_XENO_OPT_SHIRQ_LEVEL && !CONFIG_XENO_OPT_SHIRQ_EDGE */ + +int xnintr_irq_attach(xnintr_t *intr) +{ + return xnarch_hook_irq(intr->irq, &xnintr_irq_handler, intr->iack, intr= ); +} + +int xnintr_irq_detach(xnintr_t *intr) +{ + 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 */ + int xnintr_irq_proc(unsigned int irq, char *str) { - xnintr_shirq_t *shirq; xnintr_t *intr; char *p =3D str; + spl_t s; =20 if (rthal_virtual_irq_p(irq)) { p +=3D sprintf(p, " [virtual]"); @@ -736,43 +739,33 @@ int xnintr_irq_proc(unsigned int irq, ch return p - str; } =20 - shirq =3D &xnshirqs[irq]; + xnlock_get_irqsave(&intrlock, s); =20 - xnintr_shirq_lock(shirq); - intr =3D shirq->handlers; - - if (intr) - p +=3D sprintf(p, " "); - - while (intr) { - if (*(intr->name)) - p +=3D sprintf(p, " %s,", intr->name); +#if defined(CONFIG_XENO_OPT_SHIRQ_LEVEL) || defined(CONFIG_XENO_OPT_SHIR= Q_EDGE) + intr =3D xnshirqs[irq].handlers; + if (intr) { + strcpy(p, " "); p +=3D 8; + + do { + *p =3D ' '; p +=3D 1; + strcpy(p, intr->name); p +=3D strlen(intr->name); =20 - intr =3D intr->next; + intr =3D intr->next; + } while (intr); } - - xnintr_shirq_unlock(shirq); - - if (p !=3D str) - --p; - - return p - str; -} - #else /* !CONFIG_XENO_OPT_SHIRQ_LEVEL && !CONFIG_XENO_OPT_SHIRQ_EDGE */ + intr =3D rthal_irq_cookie(&rthal_domain, irq); + if (intr) { + strcpy(p, " "); p +=3D 9; + strcpy(p, intr->name); p +=3D strlen(intr->name); + } +#endif /* CONFIG_XENO_OPT_SHIRQ_LEVEL || CONFIG_XENO_OPT_SHIRQ_EDGE */ =20 -int xnintr_mount(void) -{ - return 0; -} + xnlock_put_irqrestore(&intrlock, s); =20 -int xnintr_irq_proc(unsigned int irq, char *str) -{ - return 0; + return p - str; } =20 -#endif /* CONFIG_XENO_OPT_SHIRQ_LEVEL || CONFIG_XENO_OPT_SHIRQ_EDGE */ - EXPORT_SYMBOL(xnintr_attach); EXPORT_SYMBOL(xnintr_destroy); EXPORT_SYMBOL(xnintr_detach); Index: xenomai/ChangeLog =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- xenomai.orig/ChangeLog +++ xenomai/ChangeLog @@ -1,3 +1,10 @@ +2006-09-26 Jan Kiszka + Dmitry Adamushko + + * ksrc/nucleus/intr.c: Reorganise locking around xnintr_attach, + and xnintr_detach in order to avoid races with xnintr_irq_proc, + provide IRQ owner names also for non-shared setups, refactor code. + 2006-09-25 Niklaus Giger =20 * ksrc/arch/powerpc/hal.c (rthal_set_cpu_timers_unsafe): Fix --------------010907050001060702080706 Content-Type: text/plain; name="xnintr-reorder-v2.patch" Content-Transfer-Encoding: quoted-printable Content-Disposition: inline; filename="xnintr-reorder-v2.patch" --- ksrc/nucleus/intr.c | 883 +++++++++++++++++++++++++--------------------= ------- 1 file changed, 439 insertions(+), 444 deletions(-) Index: xenomai/ksrc/nucleus/intr.c =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- xenomai.orig/ksrc/nucleus/intr.c +++ xenomai/ksrc/nucleus/intr.c @@ -36,363 +36,14 @@ #include #include =20 +#define XNINTR_MAX_UNHANDLED 1000 + #ifdef CONFIG_SMP xnlock_t intrlock; #endif /* CONFIG_SMP */ =20 xnintr_t nkclock; =20 -static void xnintr_irq_handler(unsigned irq, void *cookie); - -static int xnintr_irq_attach(xnintr_t *intr); -static int xnintr_irq_detach(xnintr_t *intr); -static void xnintr_synchronize(xnintr_t *intr); - -/*! - * \fn int xnintr_init (xnintr_t *intr,const char *name,unsigned irq,xni= sr_t isr,xniack_t iack,xnflags_t flags) - * \brief Initialize an interrupt object. - * - * Associates an interrupt object with an IRQ line. - * - * When an interrupt occurs on the given @a irq line, the ISR is fired - * in order to deal with the hardware event. The interrupt service - * code may call any non-suspensive service from the nucleus. - * - * Upon receipt of an IRQ, the ISR is immediately called on behalf of - * the interrupted stack context, the rescheduling procedure is - * locked, and the interrupt source is masked at hardware level. The - * status value returned by the ISR is then checked for the following - * values: - * - * - XN_ISR_HANDLED indicates that the interrupt request has been fulfil= led - * by the ISR. - * - * - XN_ISR_NONE indicates the opposite to XN_ISR_HANDLED. The ISR must = always - * return this value when it determines that the interrupt request has n= ot been - * issued by the dedicated hardware device. - * - * In addition, one of the following bits may be set by the ISR : - * - * NOTE: use these bits with care and only when you do understand their = effect - * on the system. - * The ISR is not encouraged to use these bits in case it shares the IRQ= line - * with other ISRs in the real-time domain. - * - * - XN_ISR_PROPAGATE tells the nucleus to require the real-time control= - * layer to forward the IRQ. For instance, this would cause the Adeos - * control layer to propagate the interrupt down the interrupt - * pipeline to other Adeos domains, such as Linux. This is the regular - * way to share interrupts between the nucleus and the host system. - * - * - XN_ISR_NOENABLE causes the nucleus to ask the real-time control - * layer _not_ to re-enable the IRQ line (read the following section). - * xnarch_end_irq() must be called to re-enable the IRQ line later. - * - * The nucleus re-enables the IRQ line by default. Over some real-time - * control layers which mask and acknowledge IRQs, this operation is - * necessary to revalidate the interrupt channel so that more interrupts= - * can be notified. - * - * A count of interrupt receipts is tracked into the interrupt - * descriptor, and reset to zero each time the interrupt object is - * attached. Since this count could wrap around, it should be used as - * an indication of interrupt activity only. - * - * @param intr The address of a interrupt object descriptor the - * nucleus will use to store the object-specific data. This - * descriptor must always be valid while the object is active - * therefore it must be allocated in permanent memory. - * - * @param name An ASCII string standing for the symbolic name of the - * interrupt object. - * - * @param irq The hardware interrupt channel associated with the - * interrupt object. This value is architecture-dependent. An - * interrupt object must then be attached to the hardware interrupt - * vector using the xnintr_attach() service for the associated IRQs - * to be directed to this object. - * - * @param isr The address of a valid low-level interrupt service - * routine if this parameter is non-zero. This handler will be called - * each time the corresponding IRQ is delivered on behalf of an - * interrupt context. When called, the ISR is passed the descriptor - * address of the interrupt object. - * - * @param iack The address of an optional interrupt acknowledge - * routine, aimed at replacing the default one. Only very specific - * situations actually require to override the default setting for - * this parameter, like having to acknowledge non-standard PIC - * hardware. @a iack should return a non-zero value to indicate that - * the interrupt has been properly acknowledged. If @a iack is NULL, - * the default routine will be used instead. - * - * @param flags A set of creation flags affecting the operation. The - * valid flags are: - * - * - XN_ISR_SHARED enables IRQ-sharing with other interrupt objects. - * - * - XN_ISR_EDGE is an additional flag need to be set together with XN_I= SR_SHARED - * to enable IRQ-sharing of edge-triggered interrupts. - * - * @return No error condition being defined, 0 is always returned. - * - * Environments: - * - * This service can be called from: - * - * - Kernel module initialization/cleanup code - * - Kernel-based task - * - User-space task - * - * Rescheduling: never. - */ - -int xnintr_init(xnintr_t *intr, - const char *name, - unsigned irq, xnisr_t isr, xniack_t iack, xnflags_t flags) -{ - intr->irq =3D irq; - intr->isr =3D isr; - intr->iack =3D iack; - intr->cookie =3D NULL; - intr->hits =3D 0; - intr->name =3D name; - intr->flags =3D flags; - intr->unhandled =3D 0; -#if defined(CONFIG_XENO_OPT_SHIRQ_LEVEL) || defined(CONFIG_XENO_OPT_SHIR= Q_EDGE) - intr->next =3D NULL; -#endif /* CONFIG_XENO_OPT_SHIRQ_LEVEL || CONFIG_XENO_OPT_SHIRQ_EDGE */ - - return 0; -} - -/*!=20 - * \fn int xnintr_destroy (xnintr_t *intr) - * \brief Destroy an interrupt object. - * - * Destroys an interrupt object previously initialized by - * xnintr_init(). The interrupt object is automatically detached by a - * call to xnintr_detach(). No more IRQs will be dispatched by this - * object after this service has returned. - * - * @param intr The descriptor address of the interrupt object to - * destroy. - * - * @return 0 is returned on success. Otherwise, -EBUSY is returned if - * an error occurred while detaching the interrupt (see - * xnintr_detach()). - * - * Environments: - * - * This service can be called from: - * - * - Kernel module initialization/cleanup code - * - Kernel-based task - * - User-space task - * - * Rescheduling: never. - */ - -int xnintr_destroy(xnintr_t *intr) -{ - xnintr_detach(intr); - return 0; -} - -/*! - * \fn int xnintr_attach (xnintr_t *intr, void *cookie); - * \brief Attach an interrupt object. - * - * Attach an interrupt object previously initialized by - * xnintr_init(). After this operation is completed, all IRQs received - * from the corresponding interrupt channel are directed to the - * object's ISR. - * - * @param intr The descriptor address of the interrupt object to - * attach. - * - * @param cookie A user-defined opaque value which is stored into the - * interrupt object descriptor for further retrieval by the ISR/ISR - * handlers. - * - * @return 0 is returned on success. Otherwise, -EINVAL is returned if - * a low-level error occurred while attaching the interrupt. -EBUSY is - * specifically returned if the interrupt object was already attached. - * - * Environments: - * - * This service can be called from: - * - * - Kernel module initialization/cleanup code - * - Kernel-based task - * - User-space task - * - * Rescheduling: never. - * - * @note Attaching an interrupt resets the tracked number of receipts - * to zero. - */ - -int xnintr_attach(xnintr_t *intr, void *cookie) -{ - int err; - spl_t s; - - intr->hits =3D 0; - intr->cookie =3D cookie; - - xnlock_get_irqsave(&intrlock, s); - - err =3D xnintr_irq_attach(intr); - - xnlock_put_irqrestore(&intrlock, s); - - return err; -} - -/*!=20 - * \fn int xnintr_detach (xnintr_t *intr) - * \brief Detach an interrupt object. - * - * Detach an interrupt object previously attached by - * xnintr_attach(). After this operation is completed, no more IRQs - * are directed to the object's ISR, but the interrupt object itself - * remains valid. A detached interrupt object can be attached again by - * a subsequent call to xnintr_attach(). - * - * @param intr The descriptor address of the interrupt object to - * detach. - * - * @return 0 is returned on success. Otherwise, -EINVAL is returned if - * a low-level error occurred while detaching the interrupt. Detaching - * a non-attached interrupt object leads to a null-effect and returns - * 0. - * - * Environments: - * - * This service can be called from: - * - * - Kernel module initialization/cleanup code - * - Kernel-based task - * - User-space task - * - * Rescheduling: never. - */ - -int xnintr_detach(xnintr_t *intr) -{ - int err; - spl_t s; - - xnlock_get_irqsave(&intrlock, s); - - err =3D xnintr_irq_detach(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; -} - -/*!=20 - * \fn int xnintr_enable (xnintr_t *intr) - * \brief Enable an interrupt object. - * - * Enables the hardware interrupt line associated with an interrupt - * object. Over real-time control layers which mask and acknowledge - * IRQs, this operation is necessary to revalidate the interrupt - * channel so that more interrupts can be notified. - - * @param intr The descriptor address of the interrupt object to - * enable. - * - * @return 0 is returned on success. Otherwise, -EINVAL is returned if - * a low-level error occurred while enabling the interrupt. - * - * Environments: - * - * This service can be called from: - * - * - Kernel module initialization/cleanup code - * - Kernel-based task - * - User-space task - * - * Rescheduling: never. - */ - -int xnintr_enable(xnintr_t *intr) -{ - return xnarch_enable_irq(intr->irq); -} - -/*!=20 - * \fn int xnintr_disable (xnintr_t *intr) - * \brief Disable an interrupt object. - * - * Disables the hardware interrupt line associated with an interrupt - * object. This operation invalidates further interrupt requests from - * the given source until the IRQ line is re-enabled anew. - * - * @param intr The descriptor address of the interrupt object to - * disable. - * - * @return 0 is returned on success. Otherwise, -EINVAL is returned if - * a low-level error occurred while disabling the interrupt. - * - * Environments: - * - * This service can be called from: - * - * - Kernel module initialization/cleanup code - * - Kernel-based task - * - User-space task - * - * Rescheduling: never. - */ - -int xnintr_disable(xnintr_t *intr) -{ - return xnarch_disable_irq(intr->irq); -} - -/*!=20 - * \fn xnarch_cpumask_t xnintr_affinity (xnintr_t *intr, xnarch_cpumask_= t cpumask) - * \brief Set interrupt's processor affinity. - * - * Causes the IRQ associated with the interrupt object @a intr to be - * received only on processors which bits are set in @a cpumask. - * - * @param intr The descriptor address of the interrupt object which - * affinity is to be changed. - * - * @param cpumask The new processor affinity of the interrupt object. - * - * @return the previous cpumask on success, or an empty mask on - * failure. - * - * @note Depending on architectures, setting more than one bit in @a - * cpumask could be meaningless. - */ - -xnarch_cpumask_t xnintr_affinity(xnintr_t *intr, xnarch_cpumask_t cpumas= k) -{ - return xnarch_set_irq_affinity(intr->irq, cpumask); -} - -/* Low-level clock irq handler. */ - -void xnintr_clock_handler(void) -{ - xnarch_announce_tick(); - xnintr_irq_handler(nkclock.irq, &nkclock); -} - -#define XNINTR_MAX_UNHANDLED 1000 /* * Low-level interrupt handler dispatching the ISRs -- Called with * interrupts off. @@ -441,8 +92,14 @@ static void xnintr_irq_handler(unsigned=20 xnltt_log_event(xeno_ev_iexit, irq); } =20 -/*@}*/ - +/* Low-level clock irq handler. */ + +void xnintr_clock_handler(void) +{ + xnarch_announce_tick(); + xnintr_irq_handler(nkclock.irq, &nkclock); +} + /* Optional support for shared interrupts. */ =20 #if defined(CONFIG_XENO_OPT_SHIRQ_LEVEL) || defined(CONFIG_XENO_OPT_SHIR= Q_EDGE) @@ -603,128 +260,465 @@ static void xnintr_edge_shirq_handler(un if (--sched->inesting =3D=3D 0 && xnsched_resched_p()) xnpod_schedule(); =20 - xnltt_log_event(xeno_ev_iexit, irq); + xnltt_log_event(xeno_ev_iexit, irq); +} + +#endif /* CONFIG_XENO_OPT_SHIRQ_EDGE */ + +static inline int xnintr_irq_attach(xnintr_t *intr) +{ + xnintr_shirq_t *shirq =3D &xnshirqs[intr->irq]; + xnintr_t *prev, **p =3D &shirq->handlers; + int err; + + if (intr->irq >=3D RTHAL_NR_IRQS) + return -EINVAL; + + if (__testbits(intr->flags, XN_ISR_ATTACHED)) + return -EPERM; + + if ((prev =3D *p) !=3D NULL) { + /* Check on whether the shared mode is allowed. */ + if (!(prev->flags & intr->flags & XN_ISR_SHARED) || + (prev->iack !=3D intr->iack) + || ((prev->flags & XN_ISR_EDGE) !=3D + (intr->flags & XN_ISR_EDGE))) + return -EBUSY; + + /* Get a position at the end of the list to insert the new element. */= + while (prev) { + p =3D &prev->next; + prev =3D *p; + } + } else { + /* Initialize the corresponding interrupt channel */ + void (*handler) (unsigned, void *) =3D &xnintr_irq_handler; + + if (intr->flags & XN_ISR_SHARED) { +#if defined(CONFIG_XENO_OPT_SHIRQ_LEVEL) + handler =3D &xnintr_shirq_handler; +#endif /* CONFIG_XENO_OPT_SHIRQ_LEVEL */ + +#if defined(CONFIG_XENO_OPT_SHIRQ_EDGE) + if (intr->flags & XN_ISR_EDGE) + handler =3D &xnintr_edge_shirq_handler; +#endif /* CONFIG_XENO_OPT_SHIRQ_EDGE */ + } + shirq->unhandled =3D 0; + + err =3D xnarch_hook_irq(intr->irq, handler, intr->iack, intr); + if (err) + return err; + } + + __setbits(intr->flags, XN_ISR_ATTACHED); + + /* Add a given interrupt object. */ + intr->next =3D NULL; + *p =3D intr; + + return 0; +} + +static inline int xnintr_irq_detach(xnintr_t *intr) +{ + xnintr_shirq_t *shirq =3D &xnshirqs[intr->irq]; + xnintr_t *e, **p =3D &shirq->handlers; + int err =3D 0; + + if (intr->irq >=3D RTHAL_NR_IRQS) + return -EINVAL; + + if (!__testbits(intr->flags, XN_ISR_ATTACHED)) + return -EPERM; + + __clrbits(intr->flags, XN_ISR_ATTACHED); + + while ((e =3D *p) !=3D NULL) { + if (e =3D=3D intr) { + /* Remove a given interrupt object from the list. */ + *p =3D e->next; + + /* Release the IRQ line if this was the last user */ + if (shirq->handlers =3D=3D NULL) + err =3D xnarch_release_irq(intr->irq); + + return err; + } + p =3D &e->next; + } + + xnlogerr("attempted to detach a non previously attached interrupt " + "object.\n"); + return err; +} + +int xnintr_mount(void) +{ + int i; + for (i =3D 0; i < RTHAL_NR_IRQS; ++i) { + xnshirqs[i].handlers =3D NULL; +#ifdef CONFIG_SMP + atomic_set(&xnshirqs[i].active, 0); +#endif + } + return 0; +} + +#else /* !CONFIG_XENO_OPT_SHIRQ_LEVEL && !CONFIG_XENO_OPT_SHIRQ_EDGE */ + +static inline int xnintr_irq_attach(xnintr_t *intr) +{ + return xnarch_hook_irq(intr->irq, &xnintr_irq_handler, intr->iack, intr= ); +} + +static inline int xnintr_irq_detach(xnintr_t *intr) +{ + 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 */ + +/*! + * \fn int xnintr_init (xnintr_t *intr,const char *name,unsigned irq,xni= sr_t isr,xniack_t iack,xnflags_t flags) + * \brief Initialize an interrupt object. + * + * Associates an interrupt object with an IRQ line. + * + * When an interrupt occurs on the given @a irq line, the ISR is fired + * in order to deal with the hardware event. The interrupt service + * code may call any non-suspensive service from the nucleus. + * + * Upon receipt of an IRQ, the ISR is immediately called on behalf of + * the interrupted stack context, the rescheduling procedure is + * locked, and the interrupt source is masked at hardware level. The + * status value returned by the ISR is then checked for the following + * values: + * + * - XN_ISR_HANDLED indicates that the interrupt request has been fulfil= led + * by the ISR. + * + * - XN_ISR_NONE indicates the opposite to XN_ISR_HANDLED. The ISR must = always + * return this value when it determines that the interrupt request has n= ot been + * issued by the dedicated hardware device. + * + * In addition, one of the following bits may be set by the ISR : + * + * NOTE: use these bits with care and only when you do understand their = effect + * on the system. + * The ISR is not encouraged to use these bits in case it shares the IRQ= line + * with other ISRs in the real-time domain. + * + * - XN_ISR_PROPAGATE tells the nucleus to require the real-time control= + * layer to forward the IRQ. For instance, this would cause the Adeos + * control layer to propagate the interrupt down the interrupt + * pipeline to other Adeos domains, such as Linux. This is the regular + * way to share interrupts between the nucleus and the host system. + * + * - XN_ISR_NOENABLE causes the nucleus to ask the real-time control + * layer _not_ to re-enable the IRQ line (read the following section). + * xnarch_end_irq() must be called to re-enable the IRQ line later. + * + * The nucleus re-enables the IRQ line by default. Over some real-time + * control layers which mask and acknowledge IRQs, this operation is + * necessary to revalidate the interrupt channel so that more interrupts= + * can be notified. + * + * A count of interrupt receipts is tracked into the interrupt + * descriptor, and reset to zero each time the interrupt object is + * attached. Since this count could wrap around, it should be used as + * an indication of interrupt activity only. + * + * @param intr The address of a interrupt object descriptor the + * nucleus will use to store the object-specific data. This + * descriptor must always be valid while the object is active + * therefore it must be allocated in permanent memory. + * + * @param name An ASCII string standing for the symbolic name of the + * interrupt object. + * + * @param irq The hardware interrupt channel associated with the + * interrupt object. This value is architecture-dependent. An + * interrupt object must then be attached to the hardware interrupt + * vector using the xnintr_attach() service for the associated IRQs + * to be directed to this object. + * + * @param isr The address of a valid low-level interrupt service + * routine if this parameter is non-zero. This handler will be called + * each time the corresponding IRQ is delivered on behalf of an + * interrupt context. When called, the ISR is passed the descriptor + * address of the interrupt object. + * + * @param iack The address of an optional interrupt acknowledge + * routine, aimed at replacing the default one. Only very specific + * situations actually require to override the default setting for + * this parameter, like having to acknowledge non-standard PIC + * hardware. @a iack should return a non-zero value to indicate that + * the interrupt has been properly acknowledged. If @a iack is NULL, + * the default routine will be used instead. + * + * @param flags A set of creation flags affecting the operation. The + * valid flags are: + * + * - XN_ISR_SHARED enables IRQ-sharing with other interrupt objects. + * + * - XN_ISR_EDGE is an additional flag need to be set together with XN_I= SR_SHARED + * to enable IRQ-sharing of edge-triggered interrupts. + * + * @return No error condition being defined, 0 is always returned. + * + * Environments: + * + * This service can be called from: + * + * - Kernel module initialization/cleanup code + * - Kernel-based task + * - User-space task + * + * Rescheduling: never. + */ + +int xnintr_init(xnintr_t *intr, + const char *name, + unsigned irq, xnisr_t isr, xniack_t iack, xnflags_t flags) +{ + intr->irq =3D irq; + intr->isr =3D isr; + intr->iack =3D iack; + intr->cookie =3D NULL; + intr->hits =3D 0; + intr->name =3D name; + intr->flags =3D flags; + intr->unhandled =3D 0; +#if defined(CONFIG_XENO_OPT_SHIRQ_LEVEL) || defined(CONFIG_XENO_OPT_SHIR= Q_EDGE) + intr->next =3D NULL; +#endif /* CONFIG_XENO_OPT_SHIRQ_LEVEL || CONFIG_XENO_OPT_SHIRQ_EDGE */ + + return 0; +} + +/*! + * \fn int xnintr_destroy (xnintr_t *intr) + * \brief Destroy an interrupt object. + * + * Destroys an interrupt object previously initialized by + * xnintr_init(). The interrupt object is automatically detached by a + * call to xnintr_detach(). No more IRQs will be dispatched by this + * object after this service has returned. + * + * @param intr The descriptor address of the interrupt object to + * destroy. + * + * @return 0 is returned on success. Otherwise, -EBUSY is returned if + * an error occurred while detaching the interrupt (see + * xnintr_detach()). + * + * Environments: + * + * This service can be called from: + * + * - Kernel module initialization/cleanup code + * - Kernel-based task + * - User-space task + * + * Rescheduling: never. + */ + +int xnintr_destroy(xnintr_t *intr) +{ + xnintr_detach(intr); + return 0; } =20 -#endif /* CONFIG_XENO_OPT_SHIRQ_EDGE */ +/*! + * \fn int xnintr_attach (xnintr_t *intr, void *cookie); + * \brief Attach an interrupt object. + * + * Attach an interrupt object previously initialized by + * xnintr_init(). After this operation is completed, all IRQs received + * from the corresponding interrupt channel are directed to the + * object's ISR. + * + * @param intr The descriptor address of the interrupt object to + * attach. + * + * @param cookie A user-defined opaque value which is stored into the + * interrupt object descriptor for further retrieval by the ISR/ISR + * handlers. + * + * @return 0 is returned on success. Otherwise, -EINVAL is returned if + * a low-level error occurred while attaching the interrupt. -EBUSY is + * specifically returned if the interrupt object was already attached. + * + * Environments: + * + * This service can be called from: + * + * - Kernel module initialization/cleanup code + * - Kernel-based task + * - User-space task + * + * Rescheduling: never. + * + * @note Attaching an interrupt resets the tracked number of receipts + * to zero. + */ =20 -int xnintr_irq_attach(xnintr_t *intr) +int xnintr_attach(xnintr_t *intr, void *cookie) { - xnintr_shirq_t *shirq =3D &xnshirqs[intr->irq]; - xnintr_t *prev, **p =3D &shirq->handlers; int err; + spl_t s; =20 - if (intr->irq >=3D RTHAL_NR_IRQS) - return -EINVAL; - - if (__testbits(intr->flags, XN_ISR_ATTACHED)) - return -EPERM; - - if ((prev =3D *p) !=3D NULL) { - /* Check on whether the shared mode is allowed. */ - if (!(prev->flags & intr->flags & XN_ISR_SHARED) || - (prev->iack !=3D intr->iack) - || ((prev->flags & XN_ISR_EDGE) !=3D - (intr->flags & XN_ISR_EDGE))) - return -EBUSY; - - /* Get a position at the end of the list to insert the new element. */= - while (prev) { - p =3D &prev->next; - prev =3D *p; - } - } else { - /* Initialize the corresponding interrupt channel */ - void (*handler) (unsigned, void *) =3D &xnintr_irq_handler; - - if (intr->flags & XN_ISR_SHARED) { -#if defined(CONFIG_XENO_OPT_SHIRQ_LEVEL) - handler =3D &xnintr_shirq_handler; -#endif /* CONFIG_XENO_OPT_SHIRQ_LEVEL */ - -#if defined(CONFIG_XENO_OPT_SHIRQ_EDGE) - if (intr->flags & XN_ISR_EDGE) - handler =3D &xnintr_edge_shirq_handler; -#endif /* CONFIG_XENO_OPT_SHIRQ_EDGE */ - } - shirq->unhandled =3D 0; + intr->hits =3D 0; + intr->cookie =3D cookie; =20 - err =3D xnarch_hook_irq(intr->irq, handler, intr->iack, intr); - if (err) - return err; - } + xnlock_get_irqsave(&intrlock, s); =20 - __setbits(intr->flags, XN_ISR_ATTACHED); + err =3D xnintr_irq_attach(intr); =20 - /* Add a given interrupt object. */ - intr->next =3D NULL; - *p =3D intr; + xnlock_put_irqrestore(&intrlock, s); =20 - return 0; + return err; } =20 -int xnintr_irq_detach(xnintr_t *intr) -{ - xnintr_shirq_t *shirq =3D &xnshirqs[intr->irq]; - xnintr_t *e, **p =3D &shirq->handlers; - int err =3D 0; - - if (intr->irq >=3D RTHAL_NR_IRQS) - return -EINVAL; +/*! + * \fn int xnintr_detach (xnintr_t *intr) + * \brief Detach an interrupt object. + * + * Detach an interrupt object previously attached by + * xnintr_attach(). After this operation is completed, no more IRQs + * are directed to the object's ISR, but the interrupt object itself + * remains valid. A detached interrupt object can be attached again by + * a subsequent call to xnintr_attach(). + * + * @param intr The descriptor address of the interrupt object to + * detach. + * + * @return 0 is returned on success. Otherwise, -EINVAL is returned if + * a low-level error occurred while detaching the interrupt. Detaching + * a non-attached interrupt object leads to a null-effect and returns + * 0. + * + * Environments: + * + * This service can be called from: + * + * - Kernel module initialization/cleanup code + * - Kernel-based task + * - User-space task + * + * Rescheduling: never. + */ =20 - if (!__testbits(intr->flags, XN_ISR_ATTACHED)) - return -EPERM; +int xnintr_detach(xnintr_t *intr) +{ + int err; + spl_t s; =20 - __clrbits(intr->flags, XN_ISR_ATTACHED); + xnlock_get_irqsave(&intrlock, s); =20 - while ((e =3D *p) !=3D NULL) { - if (e =3D=3D intr) { - /* Remove a given interrupt object from the list. */ - *p =3D e->next; + err =3D xnintr_irq_detach(intr); =20 - /* Release the IRQ line if this was the last user */ - if (shirq->handlers =3D=3D NULL) - err =3D xnarch_release_irq(intr->irq); + xnlock_put_irqrestore(&intrlock, s); =20 - return err; - } - p =3D &e->next; - } + /* 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); =20 - xnlogerr("attempted to detach a non previously attached interrupt " - "object.\n"); return err; } =20 -int xnintr_mount(void) -{ - int i; - for (i =3D 0; i < RTHAL_NR_IRQS; ++i) { - xnshirqs[i].handlers =3D NULL; -#ifdef CONFIG_SMP - atomic_set(&xnshirqs[i].active, 0); -#endif - } - return 0; -} +/*! + * \fn int xnintr_enable (xnintr_t *intr) + * \brief Enable an interrupt object. + * + * Enables the hardware interrupt line associated with an interrupt + * object. Over real-time control layers which mask and acknowledge + * IRQs, this operation is necessary to revalidate the interrupt + * channel so that more interrupts can be notified. =20 -#else /* !CONFIG_XENO_OPT_SHIRQ_LEVEL && !CONFIG_XENO_OPT_SHIRQ_EDGE */ + * @param intr The descriptor address of the interrupt object to + * enable. + * + * @return 0 is returned on success. Otherwise, -EINVAL is returned if + * a low-level error occurred while enabling the interrupt. + * + * Environments: + * + * This service can be called from: + * + * - Kernel module initialization/cleanup code + * - Kernel-based task + * - User-space task + * + * Rescheduling: never. + */ =20 -int xnintr_irq_attach(xnintr_t *intr) +int xnintr_enable(xnintr_t *intr) { - return xnarch_hook_irq(intr->irq, &xnintr_irq_handler, intr->iack, intr= ); + return xnarch_enable_irq(intr->irq); } =20 -int xnintr_irq_detach(xnintr_t *intr) +/*! + * \fn int xnintr_disable (xnintr_t *intr) + * \brief Disable an interrupt object. + * + * Disables the hardware interrupt line associated with an interrupt + * object. This operation invalidates further interrupt requests from + * the given source until the IRQ line is re-enabled anew. + * + * @param intr The descriptor address of the interrupt object to + * disable. + * + * @return 0 is returned on success. Otherwise, -EINVAL is returned if + * a low-level error occurred while disabling the interrupt. + * + * Environments: + * + * This service can be called from: + * + * - Kernel module initialization/cleanup code + * - Kernel-based task + * - User-space task + * + * Rescheduling: never. + */ + +int xnintr_disable(xnintr_t *intr) { - return xnarch_release_irq(intr->irq); + return xnarch_disable_irq(intr->irq); } =20 -void xnintr_synchronize(xnintr_t *intr) {} -int xnintr_mount(void) { return 0; } +/*! + * \fn xnarch_cpumask_t xnintr_affinity (xnintr_t *intr, xnarch_cpumask_= t cpumask) + * \brief Set interrupt's processor affinity. + * + * Causes the IRQ associated with the interrupt object @a intr to be + * received only on processors which bits are set in @a cpumask. + * + * @param intr The descriptor address of the interrupt object which + * affinity is to be changed. + * + * @param cpumask The new processor affinity of the interrupt object. + * + * @return the previous cpumask on success, or an empty mask on + * failure. + * + * @note Depending on architectures, setting more than one bit in @a + * cpumask could be meaningless. + */ =20 -#endif /* CONFIG_XENO_OPT_SHIRQ_LEVEL || CONFIG_XENO_OPT_SHIRQ_EDGE */ +xnarch_cpumask_t xnintr_affinity(xnintr_t *intr, xnarch_cpumask_t cpumas= k) +{ + return xnarch_set_irq_affinity(intr->irq, cpumask); +} =20 +#ifdef CONFIG_PROC_FS int xnintr_irq_proc(unsigned int irq, char *str) { xnintr_t *intr; @@ -765,6 +759,7 @@ int xnintr_irq_proc(unsigned int irq, ch =20 return p - str; } +#endif /* CONFIG_PROC_FS */ =20 EXPORT_SYMBOL(xnintr_attach); EXPORT_SYMBOL(xnintr_destroy); --------------010907050001060702080706-- --------------enig8012160F8C8A1AFE097577D2 Content-Type: application/pgp-signature; name="signature.asc" Content-Description: OpenPGP digital signature Content-Disposition: attachment; filename="signature.asc" -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.5 (MingW32) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iD8DBQFFGRTFniDOoMHTA+kRAg+EAJ9iaccffPaiTYheBB2Mdb7k97z5tQCfSlho HWA538zFMW0zJ9Jqn2Y6auI= =M7w7 -----END PGP SIGNATURE----- --------------enig8012160F8C8A1AFE097577D2--