ACPICA: fix leak of acpi_os_validate_address
[safe/jmp/linux-2.6] / drivers / acpi / osl.c
index 5691f16..c5b4f1e 100644 (file)
@@ -88,6 +88,7 @@ struct acpi_res_list {
        char name[5];   /* only can have a length of 4 chars, make use of this
                           one instead of res->name, no need to kalloc then */
        struct list_head resource_list;
+       int count;
 };
 
 static LIST_HEAD(resource_list_head);
@@ -1358,6 +1359,89 @@ acpi_os_validate_interface (char *interface)
        return AE_SUPPORT;
 }
 
+static inline int acpi_res_list_add(struct acpi_res_list *res)
+{
+       struct acpi_res_list *res_list_elem;
+
+       list_for_each_entry(res_list_elem, &resource_list_head,
+                           resource_list) {
+
+               if (res->resource_type == res_list_elem->resource_type &&
+                   res->start == res_list_elem->start &&
+                   res->end == res_list_elem->end) {
+
+                       /*
+                        * The Region(addr,len) already exist in the list,
+                        * just increase the count
+                        */
+
+                       res_list_elem->count++;
+                       return 0;
+               }
+       }
+
+       res->count = 1;
+       list_add(&res->resource_list, &resource_list_head);
+       return 1;
+}
+
+static inline void acpi_res_list_del(struct acpi_res_list *res)
+{
+       struct acpi_res_list *res_list_elem;
+
+       list_for_each_entry(res_list_elem, &resource_list_head,
+                           resource_list) {
+
+               if (res->resource_type == res_list_elem->resource_type &&
+                   res->start == res_list_elem->start &&
+                   res->end == res_list_elem->end) {
+
+                       /*
+                        * If the res count is decreased to 0,
+                        * remove and free it
+                        */
+
+                       if (--res_list_elem->count == 0) {
+                               list_del(&res_list_elem->resource_list);
+                               kfree(res_list_elem);
+                       }
+                       return;
+               }
+       }
+}
+
+acpi_status
+acpi_os_invalidate_address(
+    u8                   space_id,
+    acpi_physical_address   address,
+    acpi_size               length)
+{
+       struct acpi_res_list res;
+
+       switch (space_id) {
+       case ACPI_ADR_SPACE_SYSTEM_IO:
+       case ACPI_ADR_SPACE_SYSTEM_MEMORY:
+               /* Only interference checks against SystemIO and SytemMemory
+                  are needed */
+               res.start = address;
+               res.end = address + length - 1;
+               res.resource_type = space_id;
+               spin_lock(&acpi_res_lock);
+               acpi_res_list_del(&res);
+               spin_unlock(&acpi_res_lock);
+               break;
+       case ACPI_ADR_SPACE_PCI_CONFIG:
+       case ACPI_ADR_SPACE_EC:
+       case ACPI_ADR_SPACE_SMBUS:
+       case ACPI_ADR_SPACE_CMOS:
+       case ACPI_ADR_SPACE_PCI_BAR_TARGET:
+       case ACPI_ADR_SPACE_DATA_TABLE:
+       case ACPI_ADR_SPACE_FIXED_HARDWARE:
+               break;
+       }
+       return AE_OK;
+}
+
 /******************************************************************************
  *
  * FUNCTION:    acpi_os_validate_address
@@ -1382,6 +1466,7 @@ acpi_os_validate_address (
     char *name)
 {
        struct acpi_res_list *res;
+       int added;
        if (acpi_enforce_resources == ENFORCE_RESOURCES_NO)
                return AE_OK;
 
@@ -1399,14 +1484,17 @@ acpi_os_validate_address (
                res->end = address + length - 1;
                res->resource_type = space_id;
                spin_lock(&acpi_res_lock);
-               list_add(&res->resource_list, &resource_list_head);
+               added = acpi_res_list_add(res);
                spin_unlock(&acpi_res_lock);
-               pr_debug("Added %s resource: start: 0x%llx, end: 0x%llx, "
-                        "name: %s\n", (space_id == ACPI_ADR_SPACE_SYSTEM_IO)
+               pr_debug("%s %s resource: start: 0x%llx, end: 0x%llx, "
+                        "name: %s\n", added ? "Added" : "Already exist",
+                        (space_id == ACPI_ADR_SPACE_SYSTEM_IO)
                         ? "SystemIO" : "System Memory",
                         (unsigned long long)res->start,
                         (unsigned long long)res->end,
                         res->name);
+               if (!added)
+                       kfree(res);
                break;
        case ACPI_ADR_SPACE_PCI_CONFIG:
        case ACPI_ADR_SPACE_EC: