ACPICA: Tables: Back port acpi_get_table_with_size() and early_acpi_os_unmap_memory...
authorLv Zheng <lv.zheng@intel.com>
Wed, 14 Dec 2016 07:04:25 +0000 (15:04 +0800)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Wed, 21 Dec 2016 01:36:38 +0000 (02:36 +0100)
ACPICA commit cac6790954d4d752a083e6122220b8a22febcd07

This patch back ports Linux acpi_get_table_with_size() and
early_acpi_os_unmap_memory() into ACPICA upstream to reduce divergences.

The 2 APIs are used by Linux as table management APIs for long time, it
contains a hidden logic that during the early stage, the mapped tables
should be unmapped before the early stage ends.

During the early stage, tables are handled by the following sequence:
 acpi_get_table_with_size();
 parse the table
 early_acpi_os_unmap_memory();
During the late stage, tables are handled by the following sequence:
 acpi_get_table();
 parse the table
Linux uses acpi_gbl_permanent_mmap to distinguish the early stage and the
late stage.

The reasoning of introducing acpi_get_table_with_size() is: ACPICA will
remember the early mapped pointer in acpi_get_table() and Linux isn't able to
prevent ACPICA from using the wrong early mapped pointer during the late
stage as there is no API provided from ACPICA to be an inverse of
acpi_get_table() to forget the early mapped pointer.

But how ACPICA can work with the early/late stage requirement? Inside of
ACPICA, tables are ensured to be remained in "INSTALLED" state during the
early stage, and they are carefully not transitioned to "VALIDATED" state
until the late stage. So the same logic is in fact implemented inside of
ACPICA in a different way. The gap is only that the feature is not provided
to the OSPMs in an accessible external API style.

It then is possible to fix the gap by providing an inverse of
acpi_get_table() from ACPICA, so that the two Linux sequences can be
combined:
 acpi_get_table();
 parse the table
 acpi_put_table();
In order to work easier with the current Linux code, acpi_get_table() and
acpi_put_table() is implemented in a usage counting based style:
 1. When the usage count of the table is increased from 0 to 1, table is
    mapped and .Pointer is set with the mapping address (VALIDATED);
 2. When the usage count of the table is decreased from 1 to 0, .Pointer
    is unset and the mapping address is unmapped (INVALIDATED).
So that we can deploy the new APIs to Linux with minimal effort by just
invoking acpi_get_table() in acpi_get_table_with_size() and invoking
acpi_put_table() in early_acpi_os_unmap_memory(). Lv Zheng.

Link: https://github.com/acpica/acpica/commit/cac67909
Signed-off-by: Lv Zheng <lv.zheng@intel.com>
Signed-off-by: Bob Moore <robert.moore@intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/acpi/acpica/actables.h
drivers/acpi/acpica/tbutils.c
drivers/acpi/acpica/tbxface.c
drivers/acpi/osl.c
include/acpi/acpixf.h
include/acpi/actbl.h
include/acpi/platform/aclinuxex.h

index 7dd527f8ca1d24b10915c865dc3f24e4a8e34b92..94be8a8e6c082cbe5b808b049187db0657ad5cb0 100644 (file)
@@ -166,6 +166,12 @@ acpi_tb_install_table_with_override(struct acpi_table_desc *new_table_desc,
 
 acpi_status acpi_tb_parse_root_table(acpi_physical_address rsdp_address);
 
+acpi_status
+acpi_tb_get_table(struct acpi_table_desc *table_desc,
+                 struct acpi_table_header **out_table);
+
+void acpi_tb_put_table(struct acpi_table_desc *table_desc);
+
 /*
  * tbxfload
  */
index 51eb07cf989844842b6dce634a589d1e7440e154..86854e84680056e164c6adad591ad772e85a3e79 100644 (file)
@@ -381,3 +381,88 @@ next_table:
        acpi_os_unmap_memory(table, length);
        return_ACPI_STATUS(AE_OK);
 }
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_tb_get_table
+ *
+ * PARAMETERS:  table_desc          - Table descriptor
+ *              out_table           - Where the pointer to the table is returned
+ *
+ * RETURN:      Status and pointer to the requested table
+ *
+ * DESCRIPTION: Increase a reference to a table descriptor and return the
+ *              validated table pointer.
+ *              If the table descriptor is an entry of the root table list,
+ *              this API must be invoked with ACPI_MTX_TABLES acquired.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_tb_get_table(struct acpi_table_desc *table_desc,
+                 struct acpi_table_header **out_table)
+{
+       acpi_status status;
+
+       ACPI_FUNCTION_TRACE(acpi_tb_get_table);
+
+       if (table_desc->validation_count == 0) {
+
+               /* Table need to be "VALIDATED" */
+
+               status = acpi_tb_validate_table(table_desc);
+               if (ACPI_FAILURE(status)) {
+                       return_ACPI_STATUS(status);
+               }
+       }
+
+       table_desc->validation_count++;
+       if (table_desc->validation_count == 0) {
+               ACPI_ERROR((AE_INFO,
+                           "Table %p, Validation count is zero after increment\n",
+                           table_desc));
+               table_desc->validation_count--;
+               return_ACPI_STATUS(AE_LIMIT);
+       }
+
+       *out_table = table_desc->pointer;
+       return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_tb_put_table
+ *
+ * PARAMETERS:  table_desc          - Table descriptor
+ *
+ * RETURN:      None
+ *
+ * DESCRIPTION: Decrease a reference to a table descriptor and release the
+ *              validated table pointer if no references.
+ *              If the table descriptor is an entry of the root table list,
+ *              this API must be invoked with ACPI_MTX_TABLES acquired.
+ *
+ ******************************************************************************/
+
+void acpi_tb_put_table(struct acpi_table_desc *table_desc)
+{
+
+       ACPI_FUNCTION_TRACE(acpi_tb_put_table);
+
+       if (table_desc->validation_count == 0) {
+               ACPI_WARNING((AE_INFO,
+                             "Table %p, Validation count is zero before decrement\n",
+                             table_desc));
+               return_VOID;
+       }
+       table_desc->validation_count--;
+
+       if (table_desc->validation_count == 0) {
+
+               /* Table need to be "INVALIDATED" */
+
+               acpi_tb_invalidate_table(table_desc);
+       }
+
+       return_VOID;
+}
index d5adb7ac468430c82c4c1d7a1d7c75be6be40c5e..7684707b254b93cf7b7c9aa90ef3346d50609ee7 100644 (file)
@@ -282,7 +282,7 @@ ACPI_EXPORT_SYMBOL(acpi_get_table_header)
 
 /*******************************************************************************
  *
- * FUNCTION:    acpi_get_table_with_size
+ * FUNCTION:    acpi_get_table
  *
  * PARAMETERS:  signature           - ACPI signature of needed table
  *              instance            - Which instance (for SSDTs)
@@ -292,16 +292,21 @@ ACPI_EXPORT_SYMBOL(acpi_get_table_header)
  *
  * DESCRIPTION: Finds and verifies an ACPI table. Table must be in the
  *              RSDT/XSDT.
+ *              Note that an early stage acpi_get_table() call must be paired
+ *              with an early stage acpi_put_table() call. otherwise the table
+ *              pointer mapped by the early stage mapping implementation may be
+ *              erroneously unmapped by the late stage unmapping implementation
+ *              in an acpi_put_table() invoked during the late stage.
  *
  ******************************************************************************/
 acpi_status
-acpi_get_table_with_size(char *signature,
-              u32 instance, struct acpi_table_header **out_table,
-              acpi_size *tbl_size)
+acpi_get_table(char *signature,
+              u32 instance, struct acpi_table_header ** out_table)
 {
        u32 i;
        u32 j;
-       acpi_status status;
+       acpi_status status = AE_NOT_FOUND;
+       struct acpi_table_desc *table_desc;
 
        /* Parameter validation */
 
@@ -309,13 +314,22 @@ acpi_get_table_with_size(char *signature,
                return (AE_BAD_PARAMETER);
        }
 
+       /*
+        * Note that the following line is required by some OSPMs, they only
+        * check if the returned table is NULL instead of the returned status
+        * to determined if this function is succeeded.
+        */
+       *out_table = NULL;
+
+       (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+
        /* Walk the root table list */
 
        for (i = 0, j = 0; i < acpi_gbl_root_table_list.current_table_count;
             i++) {
-               if (!ACPI_COMPARE_NAME
-                   (&(acpi_gbl_root_table_list.tables[i].signature),
-                    signature)) {
+               table_desc = &acpi_gbl_root_table_list.tables[i];
+
+               if (!ACPI_COMPARE_NAME(&table_desc->signature, signature)) {
                        continue;
                }
 
@@ -323,43 +337,65 @@ acpi_get_table_with_size(char *signature,
                        continue;
                }
 
-               status =
-                   acpi_tb_validate_table(&acpi_gbl_root_table_list.tables[i]);
-               if (ACPI_SUCCESS(status)) {
-                       *out_table = acpi_gbl_root_table_list.tables[i].pointer;
-                       *tbl_size = acpi_gbl_root_table_list.tables[i].length;
-               }
-
-               if (!acpi_gbl_permanent_mmap) {
-                       acpi_gbl_root_table_list.tables[i].pointer = NULL;
-               }
-
-               return (status);
+               status = acpi_tb_get_table(table_desc, out_table);
+               break;
        }
 
-       return (AE_NOT_FOUND);
+       (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+       return (status);
 }
 
-ACPI_EXPORT_SYMBOL(acpi_get_table_with_size)
+ACPI_EXPORT_SYMBOL(acpi_get_table)
 
-acpi_status
-acpi_get_table(char *signature,
-              u32 instance, struct acpi_table_header **out_table)
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_put_table
+ *
+ * PARAMETERS:  table               - The pointer to the table
+ *
+ * RETURN:      None
+ *
+ * DESCRIPTION: Release a table returned by acpi_get_table() and its clones.
+ *              Note that it is not safe if this function was invoked after an
+ *              uninstallation happened to the original table descriptor.
+ *              Currently there is no OSPMs' requirement to handle such
+ *              situations.
+ *
+ ******************************************************************************/
+void acpi_put_table(struct acpi_table_header *table)
 {
-       acpi_size tbl_size;
+       u32 i;
+       struct acpi_table_desc *table_desc;
+
+       ACPI_FUNCTION_TRACE(acpi_put_table);
+
+       (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+
+       /* Walk the root table list */
+
+       for (i = 0; i < acpi_gbl_root_table_list.current_table_count; i++) {
+               table_desc = &acpi_gbl_root_table_list.tables[i];
 
-       return acpi_get_table_with_size(signature,
-                      instance, out_table, &tbl_size);
+               if (table_desc->pointer != table) {
+                       continue;
+               }
+
+               acpi_tb_put_table(table_desc);
+               break;
+       }
+
+       (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+       return_VOID;
 }
 
-ACPI_EXPORT_SYMBOL(acpi_get_table)
+ACPI_EXPORT_SYMBOL(acpi_put_table)
 
 /*******************************************************************************
  *
  * FUNCTION:    acpi_get_table_by_index
  *
  * PARAMETERS:  table_index         - Table index
- *              table               - Where the pointer to the table is returned
+ *              out_table           - Where the pointer to the table is returned
  *
  * RETURN:      Status and pointer to the requested table
  *
@@ -368,7 +404,7 @@ ACPI_EXPORT_SYMBOL(acpi_get_table)
  *
  ******************************************************************************/
 acpi_status
-acpi_get_table_by_index(u32 table_index, struct acpi_table_header **table)
+acpi_get_table_by_index(u32 table_index, struct acpi_table_header **out_table)
 {
        acpi_status status;
 
@@ -376,35 +412,33 @@ acpi_get_table_by_index(u32 table_index, struct acpi_table_header **table)
 
        /* Parameter validation */
 
-       if (!table) {
+       if (!out_table) {
                return_ACPI_STATUS(AE_BAD_PARAMETER);
        }
 
+       /*
+        * Note that the following line is required by some OSPMs, they only
+        * check if the returned table is NULL instead of the returned status
+        * to determined if this function is succeeded.
+        */
+       *out_table = NULL;
+
        (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
 
        /* Validate index */
 
        if (table_index >= acpi_gbl_root_table_list.current_table_count) {
-               (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
-               return_ACPI_STATUS(AE_BAD_PARAMETER);
+               status = AE_BAD_PARAMETER;
+               goto unlock_and_exit;
        }
 
-       if (!acpi_gbl_root_table_list.tables[table_index].pointer) {
-
-               /* Table is not mapped, map it */
+       status =
+           acpi_tb_get_table(&acpi_gbl_root_table_list.tables[table_index],
+                             out_table);
 
-               status =
-                   acpi_tb_validate_table(&acpi_gbl_root_table_list.
-                                          tables[table_index]);
-               if (ACPI_FAILURE(status)) {
-                       (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
-                       return_ACPI_STATUS(status);
-               }
-       }
-
-       *table = acpi_gbl_root_table_list.tables[table_index].pointer;
+unlock_and_exit:
        (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
-       return_ACPI_STATUS(AE_OK);
+       return_ACPI_STATUS(status);
 }
 
 ACPI_EXPORT_SYMBOL(acpi_get_table_by_index)
index 416953a4251094ce2066d724c1d88583b8e89705..9316dfc19ceae3242e586997961cb18b7d69bd8f 100644 (file)
@@ -433,10 +433,45 @@ void __ref acpi_os_unmap_memory(void *virt, acpi_size size)
 }
 EXPORT_SYMBOL_GPL(acpi_os_unmap_memory);
 
+/*******************************************************************************
+ *
+ * acpi_get_table_with_size()/early_acpi_os_unmap_memory():
+ *
+ * These 2 functions are traditionally used by Linux to map/unmap physical
+ * addressed ACPI tables during the early stage.
+ * They are deprectated now. Do not use them in the new code, but use
+ * acpi_get_table()/acpi_put_table() instead.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_get_table_with_size(char *signature,
+              u32 instance, struct acpi_table_header **out_table,
+              acpi_size *tbl_size)
+{
+       acpi_status status;
+
+       status = acpi_get_table(signature, instance, out_table);
+       if (ACPI_SUCCESS(status)) {
+               /*
+                * "tbl_size" is no longer used by
+                * early_acpi_os_unmap_memory(), but is still used by the
+                * ACPI table drivers. So sets it to the length of the
+                * table when the tbl_size is requested.
+                * "out_table" is not sanity checked as AE_BAD_PARAMETER
+                * is returned if it is NULL.
+                */
+               if (tbl_size && *out_table)
+                       *tbl_size = (*out_table)->length;
+       }
+
+       return (status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_get_table_with_size)
+
 void __init early_acpi_os_unmap_memory(void __iomem *virt, acpi_size size)
 {
-       if (!acpi_gbl_permanent_mmap)
-               __acpi_unmap_table(virt, size);
+       acpi_put_table(ACPI_CAST_PTR(struct acpi_table_header, virt));
 }
 
 int acpi_os_map_generic_address(struct acpi_generic_address *gas)
index 5c7356adc10b5f96f0fc39f430ae4741fbda8ea1..33828dd97fc5d9ca23b4ec030373ea5976412a82 100644 (file)
@@ -513,10 +513,12 @@ ACPI_EXTERNAL_RETURN_STATUS(acpi_status
                             acpi_get_table(acpi_string signature, u32 instance,
                                            struct acpi_table_header
                                            **out_table))
+ACPI_EXTERNAL_RETURN_VOID(void acpi_put_table(struct acpi_table_header *table))
+
 ACPI_EXTERNAL_RETURN_STATUS(acpi_status
-                            acpi_get_table_by_index(u32 table_index,
-                                                    struct acpi_table_header
-                                                    **out_table))
+                           acpi_get_table_by_index(u32 table_index,
+                                                   struct acpi_table_header
+                                                   **out_table))
 ACPI_EXTERNAL_RETURN_STATUS(acpi_status
                             acpi_install_table_handler(acpi_table_handler
                                                        handler, void *context))
@@ -974,6 +976,10 @@ ACPI_EXTERNAL_RETURN_STATUS(acpi_status
                                                     **out_table,
                                                     acpi_size *tbl_size))
 
+ACPI_EXTERNAL_RETURN_VOID(void
+                         early_acpi_os_unmap_memory(void __iomem * virt,
+                                                    acpi_size size))
+
 ACPI_EXTERNAL_RETURN_STATUS(acpi_status
                            acpi_get_data_full(acpi_handle object,
                                               acpi_object_handler handler,
index c19700e2a2fe25d169a64180593438d9815c3f77..da5708caf8a12493de0e52376c27b7c0bffd1378 100644 (file)
@@ -371,6 +371,7 @@ struct acpi_table_desc {
        union acpi_name_union signature;
        acpi_owner_id owner_id;
        u8 flags;
+       u16 validation_count;
 };
 
 /* Masks for Flags field above */
index a5509d87230a4778de5566f8cfe7b705a1dd89f1..7dbb1141f546077ceec4c9fe39b077ae8e1ba379 100644 (file)
@@ -142,7 +142,6 @@ static inline void acpi_os_terminate_command_signals(void)
 /*
  * OSL interfaces added by Linux
  */
-void early_acpi_os_unmap_memory(void __iomem * virt, acpi_size size);
 
 #endif                         /* __KERNEL__ */