Merge branches 'timers-for-linus-ntp' and 'irq-core-for-linus' of git://git.kernel...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 9 Dec 2009 03:30:19 +0000 (19:30 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 9 Dec 2009 03:30:19 +0000 (19:30 -0800)
* 'timers-for-linus-ntp' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
  ntp: Provide compability defines (You say MOD_NANO, I say ADJ_NANO)

* 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
  genirq: do not execute DEBUG_SHIRQ when irq setup failed

1  2  3 
include/linux/timex.h
kernel/irq/manage.c

diff --combined include/linux/timex.h
index 0c0ef7d4db7ce3f7ab3701b3b450d18b3b56133a,782ccd45c0d9944744c0730c27fe82acd2ffd967,aa3475fcff643a3d2b32fddff911b66e88eea48e..94f8faecdcbc75bc9a10ffc9fb2a97465480b76e
@@@@ -115,13 -115,16 -115,13 +115,16 @@@@ struct timex 
   #define ADJ_OFFSET_SS_READ   0xa001  /* read-only adjtime */
   #endif
   
- -/* xntp 3.4 compatibility names */
+ +/* NTP userland likes the MOD_ prefix better */
   #define MOD_OFFSET   ADJ_OFFSET
   #define MOD_FREQUENCY        ADJ_FREQUENCY
   #define MOD_MAXERROR ADJ_MAXERROR
   #define MOD_ESTERROR ADJ_ESTERROR
   #define MOD_STATUS   ADJ_STATUS
   #define MOD_TIMECONST        ADJ_TIMECONST
+ +#define MOD_TAI      ADJ_TAI
+ +#define MOD_MICRO    ADJ_MICRO
+ +#define MOD_NANO     ADJ_NANO
   
   
   /*
   #include <asm/timex.h>
   
   /*
  - * SHIFT_KG and SHIFT_KF establish the damping of the PLL and are chosen
  - * for a slightly underdamped convergence characteristic. SHIFT_KH
  - * establishes the damping of the FLL and is chosen by wisdom and black
  - * art.
  + * SHIFT_PLL is used as a dampening factor to define how much we
  + * adjust the frequency correction for a given offset in PLL mode.
  + * It also used in dampening the offset correction, to define how
  + * much of the current value in time_offset we correct for each
  + * second. Changing this value changes the stiffness of the ntp
  + * adjustment code. A lower value makes it more flexible, reducing
  + * NTP convergence time. A higher value makes it stiffer, increasing
  + * convergence time, but making the clock more stable.
    *
  - * MAXTC establishes the maximum time constant of the PLL. With the
  - * SHIFT_KG and SHIFT_KF values given and a time constant range from
  - * zero to MAXTC, the PLL will converge in 15 minutes to 16 hours,
  - * respectively.
  + * In David Mills' nanokernel reference implementation SHIFT_PLL is 4.
  + * However this seems to increase convergence time much too long.
  + *
  + * https://lists.ntp.org/pipermail/hackers/2008-January/003487.html
  + *
  + * In the above mailing list discussion, it seems the value of 4
  + * was appropriate for other Unix systems with HZ=100, and that
  + * SHIFT_PLL should be decreased as HZ increases. However, Linux's
  + * clock steering implementation is HZ independent.
  + *
  + * Through experimentation, a SHIFT_PLL value of 2 was found to allow
  + * for fast convergence (very similar to the NTPv3 code used prior to
  + * v2.6.19), with good clock stability.
  + *
  + *
  + * SHIFT_FLL is used as a dampening factor to define how much we
  + * adjust the frequency correction for a given offset in FLL mode.
  + * In David Mills' nanokernel reference implementation SHIFT_FLL is 2.
  + *
  + * MAXTC establishes the maximum time constant of the PLL.
    */
  -#define SHIFT_PLL    4       /* PLL frequency factor (shift) */
  +#define SHIFT_PLL    2       /* PLL frequency factor (shift) */
   #define SHIFT_FLL    2       /* FLL frequency factor (shift) */
   #define MAXTC                10      /* maximum time constant (shift) */
   
   #define SHIFT_USEC 16                /* frequency offset scale (shift) */
   #define PPM_SCALE ((s64)NSEC_PER_USEC << (NTP_SCALE_SHIFT - SHIFT_USEC))
   #define PPM_SCALE_INV_SHIFT 19
  -#define PPM_SCALE_INV ((1ll << (PPM_SCALE_INV_SHIFT + NTP_SCALE_SHIFT)) / \
  +#define PPM_SCALE_INV ((1LL << (PPM_SCALE_INV_SHIFT + NTP_SCALE_SHIFT)) / \
                       PPM_SCALE + 1)
   
  -#define MAXPHASE 500000000l  /* max phase error (ns) */
  +#define MAXPHASE 500000000L  /* max phase error (ns) */
   #define MAXFREQ 500000               /* max frequency error (ns/s) */
   #define MAXFREQ_SCALED ((s64)MAXFREQ << NTP_SCALE_SHIFT)
   #define MINSEC 256           /* min interval between updates (s) */
@@@@ -261,7 -264,11 -241,11 +264,7 @@@@ static inline int ntp_synced(void
   
   #define NTP_SCALE_SHIFT              32
   
 --#ifdef CONFIG_NO_HZ
 --#define NTP_INTERVAL_FREQ  (2)
 --#else
   #define NTP_INTERVAL_FREQ  (HZ)
 --#endif
   #define NTP_INTERVAL_LENGTH (NSEC_PER_SEC/NTP_INTERVAL_FREQ)
   
   /* Returns how long ticks are at present, in ns / 2^NTP_SCALE_SHIFT. */
@@@@ -276,9 -283,9 -260,6 +279,9 @@@@ extern int do_adjtimex(struct timex *)
   
   int read_current_timer(unsigned long *timer_val);
   
  +/* The clock frequency of the i8253/i8254 PIT */
  +#define PIT_TICK_RATE 1193182ul
  +
   #endif /* KERNEL */
   
   #endif /* LINUX_TIMEX_H */
diff --combined kernel/irq/manage.c
index bde4c667d24dd067597f8451ce2fecc12bca17fa,0ec9ed831737bead620104312249d893e3a606bc,8c68d5b95d4849fba0e4339dda947a6c1a92b907..7305b297d1eb99a8d7fd2674e1bfc2adcbe7e43f
    */
   
   #include <linux/irq.h>
  +#include <linux/kthread.h>
   #include <linux/module.h>
   #include <linux/random.h>
   #include <linux/interrupt.h>
   #include <linux/slab.h>
  +#include <linux/sched.h>
   
   #include "internals.h"
   
  -#if defined(CONFIG_SMP) && defined(CONFIG_GENERIC_HARDIRQS)
  -cpumask_var_t irq_default_affinity;
  -
   /**
    *   synchronize_irq - wait for pending IRQ handlers (on other CPUs)
    *   @irq: interrupt number to wait for
@@@@ -52,18 -52,18 -53,9 +52,18 @@@@ void synchronize_irq(unsigned int irq
   
                /* Oops, that failed? */
        } while (status & IRQ_INPROGRESS);
  +
  +     /*
  +      * We made sure that no hardirq handler is running. Now verify
  +      * that no threaded handlers are active.
  +      */
  +     wait_event(desc->wait_for_threads, !atomic_read(&desc->threads_active));
   }
   EXPORT_SYMBOL(synchronize_irq);
   
  +#ifdef CONFIG_SMP
  +cpumask_var_t irq_default_affinity;
  +
   /**
    *   irq_can_set_affinity - Check if the affinity of a given irq can be set
    *   @irq:           Interrupt to check
@@@@ -80,26 -80,26 -72,6 +80,26 @@@@ int irq_can_set_affinity(unsigned int i
        return 1;
   }
   
  +/**
  + *   irq_set_thread_affinity - Notify irq threads to adjust affinity
  + *   @desc:          irq descriptor which has affitnity changed
  + *
  + *   We just set IRQTF_AFFINITY and delegate the affinity setting
  + *   to the interrupt thread itself. We can not call
  + *   set_cpus_allowed_ptr() here as we hold desc->lock and this
  + *   code can be called from hard interrupt context.
  + */
  +void irq_set_thread_affinity(struct irq_desc *desc)
  +{
  +     struct irqaction *action = desc->action;
  +
  +     while (action) {
  +             if (action->thread)
  +                     set_bit(IRQTF_AFFINITY, &action->thread_flags);
  +             action = action->next;
  +     }
  +}
  +
   /**
    *   irq_set_affinity - Set the irq affinity of a given irq
    *   @irq:           Interrupt to set affinity
@@@@ -117,21 -117,21 -89,16 +117,21 @@@@ int irq_set_affinity(unsigned int irq, 
        spin_lock_irqsave(&desc->lock, flags);
   
   #ifdef CONFIG_GENERIC_PENDING_IRQ
  -     if (desc->status & IRQ_MOVE_PCNTXT || desc->status & IRQ_DISABLED) {
  -             cpumask_copy(desc->affinity, cpumask);
  -             desc->chip->set_affinity(irq, cpumask);
  -     } else {
  +     if (desc->status & IRQ_MOVE_PCNTXT) {
  +             if (!desc->chip->set_affinity(irq, cpumask)) {
  +                     cpumask_copy(desc->affinity, cpumask);
  +                     irq_set_thread_affinity(desc);
  +             }
  +     }
  +     else {
                desc->status |= IRQ_MOVE_PENDING;
                cpumask_copy(desc->pending_mask, cpumask);
        }
   #else
  -     cpumask_copy(desc->affinity, cpumask);
  -     desc->chip->set_affinity(irq, cpumask);
  +     if (!desc->chip->set_affinity(irq, cpumask)) {
  +             cpumask_copy(desc->affinity, cpumask);
  +             irq_set_thread_affinity(desc);
  +     }
   #endif
        desc->status |= IRQ_AFFINITY_SET;
        spin_unlock_irqrestore(&desc->lock, flags);
@@@@ -183,8 -183,8 -150,6 +183,8 @@@@ int irq_select_affinity_usr(unsigned in
   
        spin_lock_irqsave(&desc->lock, flags);
        ret = setup_affinity(irq, desc);
  +     if (!ret)
  +             irq_set_thread_affinity(desc);
        spin_unlock_irqrestore(&desc->lock, flags);
   
        return ret;
@@@@ -230,11 -230,9 -195,9 +230,11 @@@@ void disable_irq_nosync(unsigned int ir
        if (!desc)
                return;
   
 ++     chip_bus_lock(irq, desc);
        spin_lock_irqsave(&desc->lock, flags);
        __disable_irq(desc, irq, false);
        spin_unlock_irqrestore(&desc->lock, flags);
 ++     chip_bus_sync_unlock(irq, desc);
   }
   EXPORT_SYMBOL(disable_irq_nosync);
   
@@@@ -296,8 -294,7 -259,7 +296,8 @@@@ void __enable_irq(struct irq_desc *desc
    *   matches the last disable, processing of interrupts on this
    *   IRQ line is re-enabled.
    *
 -- *   This function may be called from IRQ context.
 ++ *   This function may be called from IRQ context only when
 ++ *   desc->chip->bus_lock and desc->chip->bus_sync_unlock are NULL !
    */
   void enable_irq(unsigned int irq)
   {
        if (!desc)
                return;
   
 ++     chip_bus_lock(irq, desc);
        spin_lock_irqsave(&desc->lock, flags);
        __enable_irq(desc, irq, false);
        spin_unlock_irqrestore(&desc->lock, flags);
 ++     chip_bus_sync_unlock(irq, desc);
   }
   EXPORT_SYMBOL(enable_irq);
   
@@@@ -441,165 -436,125 -401,6 +441,165 @@@@ int __irq_set_trigger(struct irq_desc *
        return ret;
   }
   
 -      int wake;
 ++/*
 ++ * Default primary interrupt handler for threaded interrupts. Is
 ++ * assigned as primary handler when request_threaded_irq is called
 ++ * with handler == NULL. Useful for oneshot interrupts.
 ++ */
 ++static irqreturn_t irq_default_primary_handler(int irq, void *dev_id)
 ++{
 ++     return IRQ_WAKE_THREAD;
 ++}
 ++
 ++/*
 ++ * Primary handler for nested threaded interrupts. Should never be
 ++ * called.
 ++ */
 ++static irqreturn_t irq_nested_primary_handler(int irq, void *dev_id)
 ++{
 ++     WARN(1, "Primary handler called for nested irq %d\n", irq);
 ++     return IRQ_NONE;
 ++}
 ++
  +static int irq_wait_for_interrupt(struct irqaction *action)
  +{
  +     while (!kthread_should_stop()) {
  +             set_current_state(TASK_INTERRUPTIBLE);
  +
  +             if (test_and_clear_bit(IRQTF_RUNTHREAD,
  +                                    &action->thread_flags)) {
  +                     __set_current_state(TASK_RUNNING);
  +                     return 0;
  +             }
  +             schedule();
  +     }
  +     return -1;
  +}
  +
 ++/*
 ++ * Oneshot interrupts keep the irq line masked until the threaded
 ++ * handler finished. unmask if the interrupt has not been disabled and
 ++ * is marked MASKED.
 ++ */
 ++static void irq_finalize_oneshot(unsigned int irq, struct irq_desc *desc)
 ++{
 ++     chip_bus_lock(irq, desc);
 ++     spin_lock_irq(&desc->lock);
 ++     if (!(desc->status & IRQ_DISABLED) && (desc->status & IRQ_MASKED)) {
 ++             desc->status &= ~IRQ_MASKED;
 ++             desc->chip->unmask(irq);
 ++     }
 ++     spin_unlock_irq(&desc->lock);
 ++     chip_bus_sync_unlock(irq, desc);
 ++}
 ++
  +#ifdef CONFIG_SMP
  +/*
  + * Check whether we need to change the affinity of the interrupt thread.
  + */
  +static void
  +irq_thread_check_affinity(struct irq_desc *desc, struct irqaction *action)
  +{
  +     cpumask_var_t mask;
  +
  +     if (!test_and_clear_bit(IRQTF_AFFINITY, &action->thread_flags))
  +             return;
  +
  +     /*
  +      * In case we are out of memory we set IRQTF_AFFINITY again and
  +      * try again next time
  +      */
  +     if (!alloc_cpumask_var(&mask, GFP_KERNEL)) {
  +             set_bit(IRQTF_AFFINITY, &action->thread_flags);
  +             return;
  +     }
  +
  +     spin_lock_irq(&desc->lock);
  +     cpumask_copy(mask, desc->affinity);
  +     spin_unlock_irq(&desc->lock);
  +
  +     set_cpus_allowed_ptr(current, mask);
  +     free_cpumask_var(mask);
  +}
  +#else
  +static inline void
  +irq_thread_check_affinity(struct irq_desc *desc, struct irqaction *action) { }
  +#endif
  +
  +/*
  + * Interrupt handler thread
  + */
  +static int irq_thread(void *data)
  +{
  +     struct sched_param param = { .sched_priority = MAX_USER_RT_PRIO/2, };
  +     struct irqaction *action = data;
  +     struct irq_desc *desc = irq_to_desc(action->irq);
 ++     int wake, oneshot = desc->status & IRQ_ONESHOT;
  +
  +     sched_setscheduler(current, SCHED_FIFO, &param);
  +     current->irqaction = action;
  +
  +     while (!irq_wait_for_interrupt(action)) {
  +
  +             irq_thread_check_affinity(desc, action);
  +
  +             atomic_inc(&desc->threads_active);
  +
  +             spin_lock_irq(&desc->lock);
  +             if (unlikely(desc->status & IRQ_DISABLED)) {
  +                     /*
  +                      * CHECKME: We might need a dedicated
  +                      * IRQ_THREAD_PENDING flag here, which
  +                      * retriggers the thread in check_irq_resend()
  +                      * but AFAICT IRQ_PENDING should be fine as it
  +                      * retriggers the interrupt itself --- tglx
  +                      */
  +                     desc->status |= IRQ_PENDING;
  +                     spin_unlock_irq(&desc->lock);
  +             } else {
  +                     spin_unlock_irq(&desc->lock);
  +
  +                     action->thread_fn(action->irq, action->dev_id);
 ++
 ++                     if (oneshot)
 ++                             irq_finalize_oneshot(action->irq, desc);
  +             }
  +
  +             wake = atomic_dec_and_test(&desc->threads_active);
  +
  +             if (wake && waitqueue_active(&desc->wait_for_threads))
  +                     wake_up(&desc->wait_for_threads);
  +     }
  +
  +     /*
  +      * Clear irqaction. Otherwise exit_irq_thread() would make
  +      * fuzz about an active irq thread going into nirvana.
  +      */
  +     current->irqaction = NULL;
  +     return 0;
  +}
  +
  +/*
  + * Called from do_exit()
  + */
  +void exit_irq_thread(void)
  +{
  +     struct task_struct *tsk = current;
  +
  +     if (!tsk->irqaction)
  +             return;
  +
  +     printk(KERN_ERR
  +            "exiting task \"%s\" (%d) is an active IRQ thread (irq %d)\n",
  +            tsk->comm ? tsk->comm : "", tsk->pid, tsk->irqaction->irq);
  +
  +     /*
  +      * Set the THREAD DIED flag to prevent further wakeups of the
  +      * soon to be gone threaded handler.
  +      */
  +     set_bit(IRQTF_DIED, &tsk->irqaction->flags);
  +}
  +
   /*
    * Internal function to register an irqaction - typically used to
    * allocate special interrupts that are part of the architecture.
@@@@ -610,7 -565,7 -411,7 +610,7 @@@@ __setup_irq(unsigned int irq, struct ir
        struct irqaction *old, **old_ptr;
        const char *old_name = NULL;
        unsigned long flags;
 --     int shared = 0;
 ++     int nested, shared = 0;
        int ret;
   
        if (!desc)
                rand_initialize_irq(irq);
        }
   
 -       * Threaded handler ?
 ++     /* Oneshot interrupts are not allowed with shared */
 ++     if ((new->flags & IRQF_ONESHOT) && (new->flags & IRQF_SHARED))
 ++             return -EINVAL;
 ++
 ++     /*
 ++      * Check whether the interrupt nests into another interrupt
 ++      * thread.
 ++      */
 ++     nested = desc->status & IRQ_NESTED_THREAD;
 ++     if (nested) {
 ++             if (!new->thread_fn)
 ++                     return -EINVAL;
 ++             /*
 ++              * Replace the primary handler which was provided from
 ++              * the driver for non nested interrupt handling by the
 ++              * dummy function which warns when called.
 ++              */
 ++             new->handler = irq_nested_primary_handler;
 ++     }
 ++
  +     /*
 -      if (new->thread_fn) {
 ++      * Create a handler thread when a thread function is supplied
 ++      * and the interrupt does not nest into another interrupt
 ++      * thread.
  +      */
 ++     if (new->thread_fn && !nested) {
  +             struct task_struct *t;
  +
  +             t = kthread_create(irq_thread, new, "irq/%d-%s", irq,
  +                                new->name);
  +             if (IS_ERR(t))
  +                     return PTR_ERR(t);
  +             /*
  +              * We keep the reference to the task struct even if
  +              * the thread dies to avoid that the interrupt code
  +              * references an already freed task_struct.
  +              */
  +             get_task_struct(t);
  +             new->thread = t;
  +     }
  +
        /*
         * The following block of code has to be executed atomically
         */
        if (!shared) {
                irq_chip_set_defaults(desc->chip);
   
  +             init_waitqueue_head(&desc->wait_for_threads);
  +
                /* Setup the type (level, edge polarity) if configured: */
                if (new->flags & IRQF_TRIGGER_MASK) {
                        ret = __irq_set_trigger(desc, irq,
                                        new->flags & IRQF_TRIGGER_MASK);
   
  -                     if (ret) {
  -                             spin_unlock_irqrestore(&desc->lock, flags);
  -                             return ret;
  -                     }
  +                     if (ret)
  +                             goto out_thread;
                } else
                        compat_irq_chip_set_default_handler(desc);
   #if defined(CONFIG_IRQ_PER_CPU)
                        desc->status |= IRQ_PER_CPU;
   #endif
   
 --             desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING |
 ++             desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING | IRQ_ONESHOT |
                                  IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED);
   
 ++             if (new->flags & IRQF_ONESHOT)
 ++                     desc->status |= IRQ_ONESHOT;
 ++
                if (!(desc->status & IRQ_NOAUTOEN)) {
                        desc->depth = 0;
                        desc->status &= ~IRQ_DISABLED;
                                (int)(new->flags & IRQF_TRIGGER_MASK));
        }
   
  +     new->irq = irq;
        *old_ptr = new;
   
        /* Reset broken irq detection when installing new handler */
   
        spin_unlock_irqrestore(&desc->lock, flags);
   
  -     new->irq = irq;
  +     /*
  +      * Strictly no need to wake it up, but hung_task complains
  +      * when no hard interrupt wakes the thread up.
  +      */
  +     if (new->thread)
  +             wake_up_process(new->thread);
  +
        register_irq_proc(irq, desc);
        new->dir = NULL;
        register_handler_proc(irq, new);
@@@@ -799,19 -729,19 -549,8 +799,19 @@@@ mismatch
                dump_stack();
        }
   #endif
  +     ret = -EBUSY;
  +
  +out_thread:
        spin_unlock_irqrestore(&desc->lock, flags);
  -     return -EBUSY;
  +     if (new->thread) {
  +             struct task_struct *t = new->thread;
  +
  +             new->thread = NULL;
  +             if (likely(!test_bit(IRQTF_DIED, &new->thread_flags)))
  +                     kthread_stop(t);
  +             put_task_struct(t);
  +     }
  +     return ret;
   }
   
   /**
@@@@ -883,7 -813,7 -622,6 +883,7 @@@@ static struct irqaction *__free_irq(uns
                else
                        desc->chip->disable(irq);
        }
  +
        spin_unlock_irqrestore(&desc->lock, flags);
   
        unregister_handler_proc(irq, action);
                local_irq_restore(flags);
        }
   #endif
  +
  +     if (action->thread) {
  +             if (!test_bit(IRQTF_DIED, &action->thread_flags))
  +                     kthread_stop(action->thread);
  +             put_task_struct(action->thread);
  +     }
  +
        return action;
   }
   
@@@@ -945,26 -875,17 -676,14 +945,26 @@@@ EXPORT_SYMBOL_GPL(remove_irq)
    */
   void free_irq(unsigned int irq, void *dev_id)
   {
 ++     struct irq_desc *desc = irq_to_desc(irq);
 ++
 ++     if (!desc)
 ++             return;
 ++
 ++     chip_bus_lock(irq, desc);
        kfree(__free_irq(irq, dev_id));
 ++     chip_bus_sync_unlock(irq, desc);
   }
   EXPORT_SYMBOL(free_irq);
   
   /**
  - *   request_irq - allocate an interrupt line
  + *   request_threaded_irq - allocate an interrupt line
    *   @irq: Interrupt line to allocate
  - *   @handler: Function to be called when the IRQ occurs
  + *   @handler: Function to be called when the IRQ occurs.
  + *             Primary handler for threaded interrupts
 ++ *             If NULL and thread_fn != NULL the default
 ++ *             primary handler is installed
  + *   @thread_fn: Function called from the irq handler thread
  + *               If NULL, no irq thread is created
    *   @irqflags: Interrupt type flags
    *   @devname: An ascii name for the claiming device
    *   @dev_id: A cookie passed back to the handler function
    *   raises, you must take care both to initialise your hardware
    *   and to set up the interrupt handler in the right order.
    *
  + *   If you want to set up a threaded irq handler for your device
  + *   then you need to supply @handler and @thread_fn. @handler ist
  + *   still called in hard interrupt context and has to check
  + *   whether the interrupt originates from the device. If yes it
  + *   needs to disable the interrupt on the device and return
  + *   IRQ_WAKE_THREAD which will wake up the handler thread and run
  + *   @thread_fn. This split handler design is necessary to support
  + *   shared interrupts.
  + *
    *   Dev_id must be globally unique. Normally the address of the
    *   device data structure is used as the cookie. Since the handler
    *   receives this value it makes sense to use it.
    *   IRQF_TRIGGER_*          Specify active edge(s) or level
    *
    */
  -int request_irq(unsigned int irq, irq_handler_t handler,
  -             unsigned long irqflags, const char *devname, void *dev_id)
  +int request_threaded_irq(unsigned int irq, irq_handler_t handler,
  +                      irq_handler_t thread_fn, unsigned long irqflags,
  +                      const char *devname, void *dev_id)
   {
        struct irqaction *action;
        struct irq_desc *desc;
   
        if (desc->status & IRQ_NOREQUEST)
                return -EINVAL;
 --     if (!handler)
 --             return -EINVAL;
 ++
 ++     if (!handler) {
 ++             if (!thread_fn)
 ++                     return -EINVAL;
 ++             handler = irq_default_primary_handler;
 ++     }
   
        action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
        if (!action)
                return -ENOMEM;
   
        action->handler = handler;
  +     action->thread_fn = thread_fn;
        action->flags = irqflags;
        action->name = devname;
        action->dev_id = dev_id;
   
 ++     chip_bus_lock(irq, desc);
        retval = __setup_irq(irq, desc, action);
 ++     chip_bus_sync_unlock(irq, desc);
 ++
        if (retval)
                kfree(action);
   
   #ifdef CONFIG_DEBUG_SHIRQ
--      if (irqflags & IRQF_SHARED) {
++      if (!retval && (irqflags & IRQF_SHARED)) {
                /*
                 * It's a shared IRQ -- the driver ought to be prepared for it
                 * to happen immediately, so let's make sure....
   #endif
        return retval;
   }
  -EXPORT_SYMBOL(request_irq);
  +EXPORT_SYMBOL(request_threaded_irq);