tpm: Fix cancellation of TPM commands (interrupt mode)
authorStefan Berger <stefanb@linux.vnet.ibm.com>
Tue, 22 Jan 2013 19:57:53 +0000 (13:57 -0600)
committerKent Yoder <key@linux.vnet.ibm.com>
Tue, 5 Feb 2013 15:38:24 +0000 (09:38 -0600)
Support cancellation of TPM commands when driver is used in interrupt
mode.

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Signed-off-by: Kent Yoder <key@linux.vnet.ibm.com>
drivers/char/tpm/tpm.c
drivers/char/tpm/tpm.h
drivers/char/tpm/tpm_tis.c

index a244cd3e7dbc57ec527a0fa696794620e949c606..0d2e82f95577ce1038d7bc382b9920967fe3f5a0 100644 (file)
@@ -1102,12 +1102,28 @@ ssize_t tpm_store_cancel(struct device *dev, struct device_attribute *attr,
 }
 EXPORT_SYMBOL_GPL(tpm_store_cancel);
 
+static bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask, bool check_cancel,
+                                  bool *canceled)
+{
+       u8 status = chip->vendor.status(chip);
+
+       *canceled = false;
+       if ((status & mask) == mask)
+               return true;
+       if (check_cancel && chip->vendor.req_canceled(chip, status)) {
+               *canceled = true;
+               return true;
+       }
+       return false;
+}
+
 int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
-                        wait_queue_head_t *queue)
+                     wait_queue_head_t *queue, bool check_cancel)
 {
        unsigned long stop;
        long rc;
        u8 status;
+       bool canceled = false;
 
        /* check current status */
        status = chip->vendor.status(chip);
@@ -1122,11 +1138,14 @@ again:
                if ((long)timeout <= 0)
                        return -ETIME;
                rc = wait_event_interruptible_timeout(*queue,
-                                                     ((chip->vendor.status(chip)
-                                                     & mask) == mask),
-                                                     timeout);
-               if (rc > 0)
+                       wait_for_tpm_stat_cond(chip, mask, check_cancel,
+                                              &canceled),
+                       timeout);
+               if (rc > 0) {
+                       if (canceled)
+                               return -ECANCELED;
                        return 0;
+               }
                if (rc == -ERESTARTSYS && freezing(current)) {
                        clear_thread_flag(TIF_SIGPENDING);
                        goto again;
index 725cb9b657dded45555a5763fe02aa17798a671b..81b52015f669ecd0b16c4948ddc39834b49001cb 100644 (file)
@@ -338,7 +338,7 @@ extern void tpm_remove_hardware(struct device *);
 extern int tpm_pm_suspend(struct device *);
 extern int tpm_pm_resume(struct device *);
 extern int wait_for_tpm_stat(struct tpm_chip *, u8, unsigned long,
-                            wait_queue_head_t *);
+                            wait_queue_head_t *, bool);
 
 #ifdef CONFIG_ACPI
 extern int tpm_add_ppi(struct kobject *);
index e4e0c65df7688c7dfc565dcf8e9d21f4b4b5143d..ae91c115c43814ff980033d264f3871382640a83 100644 (file)
@@ -198,7 +198,7 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
               wait_for_tpm_stat(chip,
                                 TPM_STS_DATA_AVAIL | TPM_STS_VALID,
                                 chip->vendor.timeout_c,
-                                &chip->vendor.read_queue)
+                                &chip->vendor.read_queue, true)
               == 0) {
                burstcnt = get_burstcount(chip);
                for (; burstcnt > 0 && size < count; burstcnt--)
@@ -241,7 +241,7 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
        }
 
        wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
-                         &chip->vendor.int_queue);
+                         &chip->vendor.int_queue, false);
        status = tpm_tis_status(chip);
        if (status & TPM_STS_DATA_AVAIL) {      /* retry? */
                dev_err(chip->dev, "Error left over data\n");
@@ -277,7 +277,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
                tpm_tis_ready(chip);
                if (wait_for_tpm_stat
                    (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b,
-                    &chip->vendor.int_queue) < 0) {
+                    &chip->vendor.int_queue, false) < 0) {
                        rc = -ETIME;
                        goto out_err;
                }
@@ -292,7 +292,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
                }
 
                wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
-                                 &chip->vendor.int_queue);
+                                 &chip->vendor.int_queue, false);
                status = tpm_tis_status(chip);
                if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) {
                        rc = -EIO;
@@ -304,7 +304,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
        iowrite8(buf[count],
                 chip->vendor.iobase + TPM_DATA_FIFO(chip->vendor.locality));
        wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
-                         &chip->vendor.int_queue);
+                         &chip->vendor.int_queue, false);
        status = tpm_tis_status(chip);
        if ((status & TPM_STS_DATA_EXPECT) != 0) {
                rc = -EIO;
@@ -342,7 +342,7 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
                if (wait_for_tpm_stat
                    (chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
                     tpm_calc_ordinal_duration(chip, ordinal),
-                    &chip->vendor.read_queue) < 0) {
+                    &chip->vendor.read_queue, false) < 0) {
                        rc = -ETIME;
                        goto out_err;
                }