ACPI / EC: Convert event handling work queue into loop style.
authorLv Zheng <lv.zheng@intel.com>
Thu, 11 Jun 2015 05:21:32 +0000 (13:21 +0800)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 15 Jun 2015 12:35:59 +0000 (14:35 +0200)
During the period that a work queue is scheduled (queued up for run) but
hasn't been run, second schedule_work() could fail. This may not lead to
the loss of queries because QR_EC is always ensured to be submitted after
the work queue has been in the running state.

The event handling work queue can be changed into the loop style to allow
us to control the code in a more flexible way:
1. Makes it possible to add event=0x00 termination condition in the loop.
2. Increases the thoughput of the QR_EC transactions as the 2nd+ QR_EC
   transactions may be handled in the same work item used for the 1st QR_EC
   transaction, thus the delay caused by the 2nd+ work item scheduling can
   be eliminated.

Except the logging message changes and the throughput improvement, this
patch is just a funcitonal no-op.

Signed-off-by: Lv Zheng <lv.zheng@intel.com>
Tested-by: Gabriele Mazzotta <gabriele.mzt@gmail.com>
Tested-by: Tigran Gabrielyan <tigrangab@gmail.com>
Tested-by: Adrien D <ghbdtn@openmailbox.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/acpi/ec.c
drivers/acpi/internal.h

index 0ce8b6e8c3e8f5589a259d06c9315594e9268021..824f3e85023e5c12557e8da91ec39c6a8ac41fe7 100644 (file)
@@ -384,7 +384,9 @@ static bool acpi_ec_submit_flushable_request(struct acpi_ec *ec)
 static void acpi_ec_submit_query(struct acpi_ec *ec)
 {
        if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) {
-               ec_dbg_req("Event started");
+               ec_dbg_evt("Command(%s) submitted/blocked",
+                          acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY));
+               ec->nr_pending_queries++;
                schedule_work(&ec->work);
        }
 }
@@ -393,7 +395,8 @@ static void acpi_ec_complete_query(struct acpi_ec *ec)
 {
        if (test_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) {
                clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags);
-               ec_dbg_req("Event stopped");
+               ec_dbg_evt("Command(%s) unblocked",
+                          acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY));
        }
 }
 
@@ -460,8 +463,8 @@ static void advance_transaction(struct acpi_ec *ec)
                                if (t->rlen == t->ri) {
                                        ec_transaction_transition(ec, ACPI_EC_COMMAND_COMPLETE);
                                        if (t->command == ACPI_EC_COMMAND_QUERY)
-                                               ec_dbg_req("Command(%s) hardware completion",
-                                                          acpi_ec_cmd_string(t->command));
+                                               ec_dbg_evt("Command(%s) completed by hardware",
+                                                          acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY));
                                        wakeup = true;
                                }
                        } else
@@ -479,8 +482,8 @@ static void advance_transaction(struct acpi_ec *ec)
                        ec_transaction_transition(ec, ACPI_EC_COMMAND_POLL);
                        t->rdata[t->ri++] = 0x00;
                        ec_transaction_transition(ec, ACPI_EC_COMMAND_COMPLETE);
-                       ec_dbg_req("Command(%s) software completion",
-                                  acpi_ec_cmd_string(t->command));
+                       ec_dbg_evt("Command(%s) completed by software",
+                                  acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY));
                        wakeup = true;
                } else if ((status & ACPI_EC_FLAG_IBF) == 0) {
                        acpi_ec_write_cmd(ec, t->command);
@@ -961,11 +964,23 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 *data)
        return result;
 }
 
-static void acpi_ec_gpe_poller(struct work_struct *work)
+static void acpi_ec_event_handler(struct work_struct *work)
 {
+       unsigned long flags;
        struct acpi_ec *ec = container_of(work, struct acpi_ec, work);
 
-       acpi_ec_query(ec, NULL);
+       ec_dbg_evt("Event started");
+
+       spin_lock_irqsave(&ec->lock, flags);
+       while (ec->nr_pending_queries) {
+               spin_unlock_irqrestore(&ec->lock, flags);
+               (void)acpi_ec_query(ec, NULL);
+               spin_lock_irqsave(&ec->lock, flags);
+               ec->nr_pending_queries--;
+       }
+       spin_unlock_irqrestore(&ec->lock, flags);
+
+       ec_dbg_evt("Event stopped");
 }
 
 static u32 acpi_ec_gpe_handler(acpi_handle gpe_device,
@@ -1040,7 +1055,7 @@ static struct acpi_ec *make_acpi_ec(void)
        init_waitqueue_head(&ec->wait);
        INIT_LIST_HEAD(&ec->list);
        spin_lock_init(&ec->lock);
-       INIT_WORK(&ec->work, acpi_ec_gpe_poller);
+       INIT_WORK(&ec->work, acpi_ec_event_handler);
        ec->timestamp = jiffies;
        return ec;
 }
index 61cb506743596d320c287b07d6107d0e888e0f6c..b09756abc6e87d5068470ad7b0337b697653e2a7 100644 (file)
@@ -139,6 +139,7 @@ struct acpi_ec {
        spinlock_t lock;
        struct work_struct work;
        unsigned long timestamp;
+       unsigned long nr_pending_queries;
 };
 
 extern struct acpi_ec *first_ec;