[PATCH v2 2/7] ACPI/EC: Add asynchronous command byte write support.
From: Lv Zheng This patch moves the first command byte write into advance_transaction() so that all EC register accesses that can affect the command processing state machine can happen in this asynchronous state machine advancement function. The advance_transaction() function then can be a complete implementation of an asyncrhonous transaction for a single command so that: 1. The first command byte can be written in the interrupt context; 2. The command completion waiter can also be used to wait the first command byte's timeout; 3. In BURST mode, the follow-up command bytes can be written in the interrupt context directly, so that it doesn't need to return to the task context. Returning to the task context reduces the throughput of the BURST mode and in the worst cases where the system workload is very high, this leads to the hardware driven automatic BURST mode exit. In order not to increase memory consumptions, this patch converts 'done' into 'flags' to contain multiple indications: 1. ACPI_EC_COMMAND_COMPLETE: converting from original 'done' condition, indicating the completion of the command transaction. 2. ACPI_EC_COMMAND_POLL: indicating the availability of writing the first command byte. A new command can utilize this flag to compete for the right of accessing the underlying hardware. There is a follow-up bug fix that has utilized this new flag. The 2 flags are important because it also reflects a key concept of IO programs' design used in the system softwares. Normally an IO program running in the kernel should first be implemented in the asynchronous way. And the 2 flags are the most common way to implement its synchronous operations on top of the asynchronous operations: 1. POLL: This flag can be used to block until the asynchronous operations can happen. 2. COMPLETE: This flag can be used to block until the asynchronous operations have completed. By constructing code cleanly in this way, many difficult problems can be solved smoothly. [zetalog: changelog updated] Reference: https://bugzilla.kernel.org/show_bug.cgi?id=70891 Signed-off-by: Lv Zheng Reported-and-tested-by: Gareth Williams Tested-by: Steffen Weber [zetalog: first affected by:] Cc: # 2.6.11: 7c6db4e0: ACPI: EC: do transaction from interrupt context [zetalog: cleanly applying to:] Cc: # 3.14.x: 42b946bb: ACPI / EC: disable GPE before removing GPE handler --- drivers/acpi/ec.c | 83 +++-- 1 file changed, 48 insertions(+), 35 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 762b4cc..f09386e 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -78,6 +78,9 @@ enum { EC_FLAGS_BLOCKED, /* Transactions are blocked */ }; +#define ACPI_EC_COMMAND_POLL 0x01 /* Available for command byte */ +#define ACPI_EC_COMMAND_COMPLETE 0x02 /* Completed last byte */ + /* ec.c is compiled in acpi namespace so this shows up as acpi.ec_delay param */ static unsigned int ec_delay __read_mostly = ACPI_EC_DELAY; module_param(ec_delay, uint, 0644); @@ -109,7 +112,7 @@ struct transaction { u8 ri; u8 wlen; u8 rlen; - bool done; + u8 flags; }; struct acpi_ec *boot_ec, *first_ec; @@ -150,63 +153,68 @@ static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data) outb(data, ec->data_addr); } -static int ec_transaction_done(struct acpi_ec *ec) +static int ec_transaction_completed(struct acpi_ec *ec) { unsigned long flags; int ret = 0; spin_lock_irqsave(>lock, flags); - if (!ec->curr || ec->curr->done) + if (!ec->curr || (ec->curr->flags & ACPI_EC_COMMAND_COMPLETE)) ret = 1; spin_unlock_irqrestore(>lock, flags); return ret; } -static void start_transaction(struct acpi_ec *ec) -{ - ec->curr->irq_count = ec->curr->wi = ec->curr->ri = 0; - ec->curr->done = false; - acpi_ec_write_cmd(ec, ec->curr->command); -} - static void advance_transaction(struct acpi_ec *ec) { - unsigned long flags; struct transaction *t; u8 status; - spin_lock_irqsave(>lock, flags); pr_debug("= %s =\n", in_interrupt() ? "IRQ" : "TASK"); status = acpi_ec_read_status(ec); t = ec->curr; if (!t) - goto unlock; - if (t->wlen > t->wi) { - if ((status & ACPI_EC_FLAG_IBF) == 0) - acpi_ec_write_data(ec, - t->wdata[t->wi++]); - else - goto err; - } else if (t->rlen > t->ri) { - if ((status & ACPI_EC_FLAG_OBF) == 1) { - t->rdata[t->ri++] = acpi_ec_read_data(ec); - if (t->rlen == t->ri) - t->done = true; + goto err; + if (t->flags & ACPI_EC_COMMAND_POLL) { + if (t->wlen >
[PATCH v2 2/7] ACPI/EC: Add asynchronous command byte write support.
From: Lv Zheng lv.zh...@intel.com This patch moves the first command byte write into advance_transaction() so that all EC register accesses that can affect the command processing state machine can happen in this asynchronous state machine advancement function. The advance_transaction() function then can be a complete implementation of an asyncrhonous transaction for a single command so that: 1. The first command byte can be written in the interrupt context; 2. The command completion waiter can also be used to wait the first command byte's timeout; 3. In BURST mode, the follow-up command bytes can be written in the interrupt context directly, so that it doesn't need to return to the task context. Returning to the task context reduces the throughput of the BURST mode and in the worst cases where the system workload is very high, this leads to the hardware driven automatic BURST mode exit. In order not to increase memory consumptions, this patch converts 'done' into 'flags' to contain multiple indications: 1. ACPI_EC_COMMAND_COMPLETE: converting from original 'done' condition, indicating the completion of the command transaction. 2. ACPI_EC_COMMAND_POLL: indicating the availability of writing the first command byte. A new command can utilize this flag to compete for the right of accessing the underlying hardware. There is a follow-up bug fix that has utilized this new flag. The 2 flags are important because it also reflects a key concept of IO programs' design used in the system softwares. Normally an IO program running in the kernel should first be implemented in the asynchronous way. And the 2 flags are the most common way to implement its synchronous operations on top of the asynchronous operations: 1. POLL: This flag can be used to block until the asynchronous operations can happen. 2. COMPLETE: This flag can be used to block until the asynchronous operations have completed. By constructing code cleanly in this way, many difficult problems can be solved smoothly. [zetalog: changelog updated] Reference: https://bugzilla.kernel.org/show_bug.cgi?id=70891 Signed-off-by: Lv Zheng lv.zh...@intel.com Reported-and-tested-by: Gareth Williams gar...@garethwilliams.me.uk Tested-by: Steffen Weber steffen.we...@gmail.com [zetalog: first affected by:] Cc: sta...@vger.kernel.org # 2.6.11: 7c6db4e0: ACPI: EC: do transaction from interrupt context [zetalog: cleanly applying to:] Cc: sta...@vger.kernel.org # 3.14.x: 42b946bb: ACPI / EC: disable GPE before removing GPE handler --- drivers/acpi/ec.c | 83 +++-- 1 file changed, 48 insertions(+), 35 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 762b4cc..f09386e 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -78,6 +78,9 @@ enum { EC_FLAGS_BLOCKED, /* Transactions are blocked */ }; +#define ACPI_EC_COMMAND_POLL 0x01 /* Available for command byte */ +#define ACPI_EC_COMMAND_COMPLETE 0x02 /* Completed last byte */ + /* ec.c is compiled in acpi namespace so this shows up as acpi.ec_delay param */ static unsigned int ec_delay __read_mostly = ACPI_EC_DELAY; module_param(ec_delay, uint, 0644); @@ -109,7 +112,7 @@ struct transaction { u8 ri; u8 wlen; u8 rlen; - bool done; + u8 flags; }; struct acpi_ec *boot_ec, *first_ec; @@ -150,63 +153,68 @@ static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data) outb(data, ec-data_addr); } -static int ec_transaction_done(struct acpi_ec *ec) +static int ec_transaction_completed(struct acpi_ec *ec) { unsigned long flags; int ret = 0; spin_lock_irqsave(ec-lock, flags); - if (!ec-curr || ec-curr-done) + if (!ec-curr || (ec-curr-flags ACPI_EC_COMMAND_COMPLETE)) ret = 1; spin_unlock_irqrestore(ec-lock, flags); return ret; } -static void start_transaction(struct acpi_ec *ec) -{ - ec-curr-irq_count = ec-curr-wi = ec-curr-ri = 0; - ec-curr-done = false; - acpi_ec_write_cmd(ec, ec-curr-command); -} - static void advance_transaction(struct acpi_ec *ec) { - unsigned long flags; struct transaction *t; u8 status; - spin_lock_irqsave(ec-lock, flags); pr_debug(= %s =\n, in_interrupt() ? IRQ : TASK); status = acpi_ec_read_status(ec); t = ec-curr; if (!t) - goto unlock; - if (t-wlen t-wi) { - if ((status ACPI_EC_FLAG_IBF) == 0) - acpi_ec_write_data(ec, - t-wdata[t-wi++]); - else - goto err; - } else if (t-rlen t-ri) { - if ((status ACPI_EC_FLAG_OBF) == 1) { - t-rdata[t-ri++] = acpi_ec_read_data(ec); - if (t-rlen == t-ri) - t-done = true; +