This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / arch / sparc64 / kernel / irq.c
index d52b842..b7f6a1e 100644 (file)
@@ -118,6 +118,10 @@ static void register_irq_proc (unsigned int irq);
                action->flags |= __irq_ino(irq) << 48;
 #define get_ino_in_irqaction(action)   (action->flags >> 48)
 
+#if NR_CPUS > 64
+#error irqaction embedded smp affinity does not work with > 64 cpus, FIXME
+#endif
+
 #define put_smpaff_in_irqaction(action, smpaff)        (action)->mask = (smpaff)
 #define get_smpaff_in_irqaction(action)        ((action)->mask)
 
@@ -454,7 +458,7 @@ int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_
        action->next = NULL;
        action->dev_id = dev_id;
        put_ino_in_irqaction(action, irq);
-       put_smpaff_in_irqaction(action, CPU_MASK_NONE);
+       put_smpaff_in_irqaction(action, 0);
 
        if (tmp)
                tmp->next = action;
@@ -687,10 +691,9 @@ static inline void redirect_intr(int cpu, struct ino_bucket *bp)
         *    Just Do It.
         */
        struct irqaction *ap = bp->irq_info;
-       cpumask_t cpu_mask;
+       cpumask_t cpu_mask = get_smpaff_in_irqaction(ap);
        unsigned int buddy, ticks;
 
-       cpu_mask = get_smpaff_in_irqaction(ap);
        cpus_and(cpu_mask, cpu_mask, cpu_online_map);
        if (cpus_empty(cpu_mask))
                cpu_mask = cpu_online_map;
@@ -711,7 +714,7 @@ static inline void redirect_intr(int cpu, struct ino_bucket *bp)
                if (++buddy >= NR_CPUS)
                        buddy = 0;
                if (++ticks > NR_CPUS) {
-                       put_smpaff_in_irqaction(ap, CPU_MASK_NONE);
+                       put_smpaff_in_irqaction(ap, 0);
                        goto out;
                }
        }
@@ -945,7 +948,7 @@ int request_fast_irq(unsigned int irq,
        action->name = name;
        action->next = NULL;
        put_ino_in_irqaction(action, irq);
-       put_smpaff_in_irqaction(action, CPU_MASK_NONE);
+       put_smpaff_in_irqaction(action, 0);
 
        *(bucket->pil + irq_action) = action;
        enable_irq(irq);
@@ -1163,15 +1166,53 @@ static struct proc_dir_entry * irq_dir [NUM_IVECS];
 
 #ifdef CONFIG_SMP
 
+#define HEX_DIGITS 16
+
+static unsigned int parse_hex_value (const char __user *buffer,
+               unsigned long count, unsigned long *ret)
+{
+       unsigned char hexnum [HEX_DIGITS];
+       unsigned long value;
+       int i;
+
+       if (!count)
+               return -EINVAL;
+       if (count > HEX_DIGITS)
+               count = HEX_DIGITS;
+       if (copy_from_user(hexnum, buffer, count))
+               return -EFAULT;
+
+       /*
+        * Parse the first 8 characters as a hex string, any non-hex char
+        * is end-of-string. '00e1', 'e1', '00E1', 'E1' are all the same.
+        */
+       value = 0;
+
+       for (i = 0; i < count; i++) {
+               unsigned int c = hexnum[i];
+
+               switch (c) {
+                       case '0' ... '9': c -= '0'; break;
+                       case 'a' ... 'f': c -= 'a'-10; break;
+                       case 'A' ... 'F': c -= 'A'-10; break;
+               default:
+                       goto out;
+               }
+               value = (value << 4) | c;
+       }
+out:
+       *ret = value;
+       return 0;
+}
+
 static int irq_affinity_read_proc (char *page, char **start, off_t off,
                        int count, int *eof, void *data)
 {
        struct ino_bucket *bp = ivector_table + (long)data;
        struct irqaction *ap = bp->irq_info;
-       cpumask_t mask;
+       cpumask_t mask = get_smpaff_in_irqaction(ap);
        int len;
 
-       mask = get_smpaff_in_irqaction(ap);
        if (cpus_empty(mask))
                mask = cpu_online_map;
 
@@ -1182,7 +1223,7 @@ static int irq_affinity_read_proc (char *page, char **start, off_t off,
        return len;
 }
 
-static inline void set_intr_affinity(int irq, cpumask_t hw_aff)
+static inline void set_intr_affinity(int irq, unsigned long hw_aff)
 {
        struct ino_bucket *bp = ivector_table + irq;
 
@@ -1200,17 +1241,22 @@ static int irq_affinity_write_proc (struct file *file, const char __user *buffer
                                        unsigned long count, void *data)
 {
        int irq = (long) data, full_count = count, err;
-       cpumask_t new_value;
+       unsigned long new_value, i;
 
-       err = cpumask_parse(buffer, count, new_value);
+       err = parse_hex_value(buffer, count, &new_value);
 
        /*
         * Do not allow disabling IRQs completely - it's a too easy
         * way to make the system unusable accidentally :-) At least
         * one online CPU still has to be targeted.
         */
-       cpus_and(new_value, new_value, cpu_online_map);
-       if (cpus_empty(new_value))
+       for (i = 0; i < NR_CPUS; i++) {
+               if ((new_value & (1UL << i)) != 0 &&
+                   !cpu_online(i))
+                       new_value &= ~(1UL << i);
+       }
+
+       if (!new_value)
                return -EINVAL;
 
        set_intr_affinity(irq, new_value);
@@ -1256,6 +1302,6 @@ static void register_irq_proc (unsigned int irq)
 void init_irq_proc (void)
 {
        /* create /proc/irq */
-       root_irq_dir = proc_mkdir("irq", NULL);
+       root_irq_dir = proc_mkdir("irq", 0);
 }