ACPI: Avoid wiping out pr->performance during preregistering
Stanislaw Gruszka [Tue, 24 Mar 2009 12:41:59 +0000 (13:41 +0100)]
When cpufreq driver call acpi_processor_preregister_performance() , function
will clean up pr->performance even if there is possibly already registered
other cpufreq driver. The patch fix this potential problem. It also remove
double checks in P domain basic validity code and move these checks to function
where _PSD data is captured.

Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
Signed-off-by: Len Brown <len.brown@intel.com>

drivers/acpi/processor_perflib.c

index 9cc769b..215f1bf 100644 (file)
@@ -479,6 +479,13 @@ static int acpi_processor_get_psd(struct acpi_processor    *pr)
                goto end;
        }
 
+       if (pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ALL &&
+           pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ANY &&
+           pdomain->coord_type != DOMAIN_COORD_TYPE_HW_ALL) {
+               printk(KERN_ERR PREFIX "Invalid _PSD:coord_type\n");
+               result = -EFAULT;
+               goto end;
+       }
 end:
        kfree(buffer.pointer);
        return result;
@@ -501,9 +508,10 @@ int acpi_processor_preregister_performance(
 
        mutex_lock(&performance_mutex);
 
-       retval = 0;
-
-       /* Call _PSD for all CPUs */
+       /*
+        * Check if another driver has already registered, and abort before
+        * changing pr->performance if it has. Check input data as well.
+        */
        for_each_possible_cpu(i) {
                pr = per_cpu(processors, i);
                if (!pr) {
@@ -513,13 +521,20 @@ int acpi_processor_preregister_performance(
 
                if (pr->performance) {
                        retval = -EBUSY;
-                       continue;
+                       goto err_out;
                }
 
                if (!performance || !percpu_ptr(performance, i)) {
                        retval = -EINVAL;
-                       continue;
+                       goto err_out;
                }
+       }
+
+       /* Call _PSD for all CPUs */
+       for_each_possible_cpu(i) {
+               pr = per_cpu(processors, i);
+               if (!pr)
+                       continue;
 
                pr->performance = percpu_ptr(performance, i);
                cpumask_set_cpu(i, pr->performance->shared_cpu_map);
@@ -535,26 +550,6 @@ int acpi_processor_preregister_performance(
         * Now that we have _PSD data from all CPUs, lets setup P-state 
         * domain info.
         */
-       for_each_possible_cpu(i) {
-               pr = per_cpu(processors, i);
-               if (!pr)
-                       continue;
-
-               /* Basic validity check for domain info */
-               pdomain = &(pr->performance->domain_info);
-               if ((pdomain->revision != ACPI_PSD_REV0_REVISION) ||
-                   (pdomain->num_entries != ACPI_PSD_REV0_ENTRIES)) {
-                       retval = -EINVAL;
-                       goto err_ret;
-               }
-               if (pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ALL &&
-                   pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ANY &&
-                   pdomain->coord_type != DOMAIN_COORD_TYPE_HW_ALL) {
-                       retval = -EINVAL;
-                       goto err_ret;
-               }
-       }
-
        cpumask_clear(covered_cpus);
        for_each_possible_cpu(i) {
                pr = per_cpu(processors, i);
@@ -643,6 +638,7 @@ err_ret:
                pr->performance = NULL; /* Will be set for real in register */
        }
 
+err_out:
        mutex_unlock(&performance_mutex);
        free_cpumask_var(covered_cpus);
        return retval;