x86/apic/x2apic: Limit the vector reservation to the user specified mask
[linux-2.6-block.git] / arch / x86 / kernel / apic / io_apic.c
index 8a08f09aa50513ffa5f7768a8ba1972290d86629..9684f963befefbf11b34042d68416ff4f2791103 100644 (file)
@@ -1113,7 +1113,6 @@ __assign_irq_vector(int irq, struct irq_cfg *cfg, const struct cpumask *mask)
         */
        static int current_vector = FIRST_EXTERNAL_VECTOR + VECTOR_OFFSET_START;
        static int current_offset = VECTOR_OFFSET_START % 16;
-       unsigned int old_vector;
        int cpu, err;
        cpumask_var_t tmp_mask;
 
@@ -1123,28 +1122,28 @@ __assign_irq_vector(int irq, struct irq_cfg *cfg, const struct cpumask *mask)
        if (!alloc_cpumask_var(&tmp_mask, GFP_ATOMIC))
                return -ENOMEM;
 
-       old_vector = cfg->vector;
-       if (old_vector) {
-               cpumask_and(tmp_mask, mask, cpu_online_mask);
-               if (cpumask_subset(tmp_mask, cfg->domain)) {
-                       free_cpumask_var(tmp_mask);
-                       return 0;
-               }
-       }
-
        /* Only try and allocate irqs on cpus that are present */
        err = -ENOSPC;
        cpumask_clear(cfg->old_domain);
        cpu = cpumask_first_and(mask, cpu_online_mask);
        while (cpu < nr_cpu_ids) {
-               int new_cpu;
-               int vector, offset;
+               int new_cpu, vector, offset;
 
-               apic->vector_allocation_domain(cpu, tmp_mask);
+               apic->vector_allocation_domain(cpu, tmp_mask, mask);
 
                if (cpumask_subset(tmp_mask, cfg->domain)) {
-                       free_cpumask_var(tmp_mask);
-                       return 0;
+                       err = 0;
+                       if (cpumask_equal(tmp_mask, cfg->domain))
+                               break;
+                       /*
+                        * New cpumask using the vector is a proper subset of
+                        * the current in use mask. So cleanup the vector
+                        * allocation for the members that are not used anymore.
+                        */
+                       cpumask_andnot(cfg->old_domain, cfg->domain, tmp_mask);
+                       cfg->move_in_progress = 1;
+                       cpumask_and(cfg->domain, cfg->domain, tmp_mask);
+                       break;
                }
 
                vector = current_vector;
@@ -1172,7 +1171,7 @@ next:
                /* Found one! */
                current_vector = vector;
                current_offset = offset;
-               if (old_vector) {
+               if (cfg->vector) {
                        cfg->move_in_progress = 1;
                        cpumask_copy(cfg->old_domain, cfg->domain);
                }