Skip to content

Commit d4640c3

Browse files
committed
tpm: Check for completion after timeout
The current implementation of timeout detection works in the following way: 1. Read completion status. If completed, return the data 2. Sleep for some time (usleep_range) 3. Check for timeout using current jiffies value. Return an error if timed out 4. Goto 1 usleep_range doesn't guarantee it's always going to wake up strictly in (min, max) range, so such a situation is possible: 1. Driver reads completion status. No completion yet 2. Process sleeps indefinitely. In the meantime, TPM responds 3. We check for timeout without checking for the completion again. Result is lost. Such a situation also happens for the guest VMs: if vCPU goes to sleep and doesn't get scheduled for some time, the guest TPM driver will timeout instantly after waking up without checking for the completion (which may already be in place). Perform the completion check once again after exiting the busy loop in order to give the device the last chance to send us some data. Since now we check for completion in two places, extract this check into a separate function. Signed-off-by: Ivan Orlov <ivan.orlov0322@gmail.com> Reviewed-by: Jonathan McDowell <noodles@meta.com> Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
1 parent 63d1dbf commit d4640c3

1 file changed

Lines changed: 15 additions & 2 deletions

File tree

drivers/char/tpm/tpm-interface.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,13 @@ static bool tpm_chip_req_canceled(struct tpm_chip *chip, u8 status)
8282
return chip->ops->req_canceled(chip, status);
8383
}
8484

85+
static bool tpm_transmit_completed(u8 status, struct tpm_chip *chip)
86+
{
87+
u8 status_masked = status & chip->ops->req_complete_mask;
88+
89+
return status_masked == chip->ops->req_complete_val;
90+
}
91+
8592
static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz)
8693
{
8794
struct tpm_header *header = buf;
@@ -140,8 +147,7 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz)
140147
stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal);
141148
do {
142149
u8 status = tpm_chip_status(chip);
143-
if ((status & chip->ops->req_complete_mask) ==
144-
chip->ops->req_complete_val)
150+
if (tpm_transmit_completed(status, chip))
145151
goto out_recv;
146152

147153
if (tpm_chip_req_canceled(chip, status)) {
@@ -153,6 +159,13 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz)
153159
rmb();
154160
} while (time_before(jiffies, stop));
155161

162+
/*
163+
* Check for completion one more time, just in case the device reported
164+
* it while the driver was sleeping in the busy loop above.
165+
*/
166+
if (tpm_transmit_completed(tpm_chip_status(chip), chip))
167+
goto out_recv;
168+
156169
tpm_chip_cancel(chip);
157170
dev_err(&chip->dev, "Operation Timed out\n");
158171
return -ETIME;

0 commit comments

Comments
 (0)