Skip to content

Commit 8a2d7ee

Browse files
tpm,tpm_tis: Handle interrupt storm
After activation of interrupts for TPM TIS drivers 0-day reports an interrupt storm on an Intel(R) Xeon(R) Gold 6346. Fix this by detecting the storm and falling back to polling: Count the number of unhandled interrupts within a 10 ms time interval. In case that more than 1000 were unhandled deactivate interrupts entirely, deregister the handler and use polling instead. The storm detection logic equals the implementation in note_interrupt() which uses timestamps and counters stored in struct irq_desc. Since this structure is private to the generic interrupt core the TPM TIS core uses its own timestamps and counters. Furthermore the TPM interrupt handler always returns IRQ_HANDLED to prevent the generic interrupt core from processing the interrupt storm. Since the interrupt deregistration function devm_free_irq() waits for all interrupt handlers to finish, only trigger a worker in the interrupt handler and do the unregistration in the worker to avoid a deadlock. Reported-by: kernel test robot <[email protected]> Closes: https://lore.kernel.org/oe-lkp/[email protected]/ Suggested-by: Lukas Wunner <[email protected]> Signed-off-by: Lino Sanfilippo <[email protected]>
1 parent c8b54bc commit 8a2d7ee

File tree

2 files changed

+85
-13
lines changed

2 files changed

+85
-13
lines changed

drivers/char/tpm/tpm_tis_core.c

Lines changed: 81 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -469,25 +469,32 @@ static int tpm_tis_send_data(struct tpm_chip *chip, const u8 *buf, size_t len)
469469
return rc;
470470
}
471471

472+
static void __tpm_tis_disable_interrupts(struct tpm_chip *chip)
473+
{
474+
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
475+
u32 intmask = 0;
476+
477+
tpm_tis_read32(priv, TPM_INT_ENABLE(priv->locality), &intmask);
478+
intmask &= ~TPM_GLOBAL_INT_ENABLE;
479+
480+
tpm_tis_request_locality(chip, 0);
481+
tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask);
482+
tpm_tis_relinquish_locality(chip, 0);
483+
484+
chip->flags &= ~TPM_CHIP_FLAG_IRQ;
485+
}
486+
472487
static void disable_interrupts(struct tpm_chip *chip)
473488
{
474489
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
475-
u32 intmask;
476-
int rc;
477490

478491
if (priv->irq == 0)
479492
return;
480493

481-
rc = tpm_tis_read32(priv, TPM_INT_ENABLE(priv->locality), &intmask);
482-
if (rc < 0)
483-
intmask = 0;
484-
485-
intmask &= ~TPM_GLOBAL_INT_ENABLE;
486-
rc = tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask);
494+
__tpm_tis_disable_interrupts(chip);
487495

488496
devm_free_irq(chip->dev.parent, priv->irq, chip);
489497
priv->irq = 0;
490-
chip->flags &= ~TPM_CHIP_FLAG_IRQ;
491498
}
492499

493500
/*
@@ -753,6 +760,52 @@ static bool tpm_tis_req_canceled(struct tpm_chip *chip, u8 status)
753760
return status == TPM_STS_COMMAND_READY;
754761
}
755762

763+
static void tpm_tis_reenable_polling(struct tpm_chip *chip)
764+
{
765+
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
766+
767+
dev_warn(&chip->dev, HW_ERR
768+
"TPM interrupt storm detected, polling instead\n");
769+
770+
__tpm_tis_disable_interrupts(chip);
771+
772+
/*
773+
* devm_free_irq() must not be called from within the interrupt handler,
774+
* since this function waits for running handlers to finish and thus it
775+
* would deadlock. Instead trigger a worker that takes care of the
776+
* unregistration.
777+
*/
778+
schedule_work(&priv->free_irq_work);
779+
}
780+
781+
static irqreturn_t tpm_tis_check_for_interrupt_storm(struct tpm_chip *chip)
782+
{
783+
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
784+
const unsigned int MAX_UNHANDLED_IRQS = 1000;
785+
786+
/*
787+
* The worker to free the TPM interrupt (free_irq_work) may already
788+
* be scheduled, so make sure it is not scheduled again.
789+
*/
790+
if (!(chip->flags & TPM_CHIP_FLAG_IRQ))
791+
return IRQ_HANDLED;
792+
793+
if (time_after(jiffies, priv->last_unhandled_irq + HZ/10))
794+
priv->unhandled_irqs = 1;
795+
else
796+
priv->unhandled_irqs++;
797+
798+
priv->last_unhandled_irq = jiffies;
799+
800+
if (priv->unhandled_irqs > MAX_UNHANDLED_IRQS)
801+
tpm_tis_reenable_polling(chip);
802+
/*
803+
* Prevent the genirq code from starting its own interrupt storm
804+
* handling by always reporting that the interrupt was handled.
805+
*/
806+
return IRQ_HANDLED;
807+
}
808+
756809
static irqreturn_t tis_int_handler(int dummy, void *dev_id)
757810
{
758811
struct tpm_chip *chip = dev_id;
@@ -762,10 +815,12 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id)
762815

763816
rc = tpm_tis_read32(priv, TPM_INT_STATUS(priv->locality), &interrupt);
764817
if (rc < 0)
765-
return IRQ_NONE;
818+
goto unhandled;
766819

767-
if (interrupt == 0)
768-
return IRQ_NONE;
820+
if (interrupt == 0) {
821+
rc = -1;
822+
goto unhandled;
823+
}
769824

770825
set_bit(TPM_TIS_IRQ_TESTED, &priv->flags);
771826
if (interrupt & TPM_INTF_DATA_AVAIL_INT)
@@ -780,8 +835,9 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id)
780835
tpm_tis_request_locality(chip, 0);
781836
rc = tpm_tis_write32(priv, TPM_INT_STATUS(priv->locality), interrupt);
782837
tpm_tis_relinquish_locality(chip, 0);
838+
unhandled:
783839
if (rc < 0)
784-
return IRQ_NONE;
840+
return tpm_tis_check_for_interrupt_storm(chip);
785841

786842
tpm_tis_read32(priv, TPM_INT_STATUS(priv->locality), &interrupt);
787843
return IRQ_HANDLED;
@@ -805,6 +861,15 @@ static void tpm_tis_gen_interrupt(struct tpm_chip *chip)
805861
chip->flags &= ~TPM_CHIP_FLAG_IRQ;
806862
}
807863

864+
static void tpm_tis_free_irq_func(struct work_struct *work)
865+
{
866+
struct tpm_tis_data *priv = container_of(work, typeof(*priv), free_irq_work);
867+
struct tpm_chip *chip = priv->chip;
868+
869+
devm_free_irq(chip->dev.parent, priv->irq, chip);
870+
priv->irq = 0;
871+
}
872+
808873
/* Register the IRQ and issue a command that will cause an interrupt. If an
809874
* irq is seen then leave the chip setup for IRQ operation, otherwise reverse
810875
* everything and leave in polling mode. Returns 0 on success.
@@ -817,6 +882,7 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask,
817882
int rc;
818883
u32 int_status;
819884

885+
INIT_WORK(&priv->free_irq_work, tpm_tis_free_irq_func);
820886

821887
rc = devm_request_threaded_irq(chip->dev.parent, irq, NULL,
822888
tis_int_handler, IRQF_ONESHOT | flags,
@@ -919,6 +985,7 @@ void tpm_tis_remove(struct tpm_chip *chip)
919985
interrupt = 0;
920986

921987
tpm_tis_write32(priv, reg, ~TPM_GLOBAL_INT_ENABLE & interrupt);
988+
flush_work(&priv->free_irq_work);
922989

923990
tpm_tis_clkrun_enable(chip, false);
924991

@@ -1023,6 +1090,7 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
10231090
chip->timeout_b = msecs_to_jiffies(TIS_TIMEOUT_B_MAX);
10241091
chip->timeout_c = msecs_to_jiffies(TIS_TIMEOUT_C_MAX);
10251092
chip->timeout_d = msecs_to_jiffies(TIS_TIMEOUT_D_MAX);
1093+
priv->chip = chip;
10261094
priv->timeout_min = TPM_TIMEOUT_USECS_MIN;
10271095
priv->timeout_max = TPM_TIMEOUT_USECS_MAX;
10281096
priv->phy_ops = phy_ops;

drivers/char/tpm/tpm_tis_core.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,15 @@ enum tpm_tis_flags {
9191
};
9292

9393
struct tpm_tis_data {
94+
struct tpm_chip *chip;
9495
u16 manufacturer_id;
9596
struct mutex locality_count_mutex;
9697
unsigned int locality_count;
9798
int locality;
9899
int irq;
100+
struct work_struct free_irq_work;
101+
unsigned long last_unhandled_irq;
102+
unsigned int unhandled_irqs;
99103
unsigned int int_mask;
100104
unsigned long flags;
101105
void __iomem *ilb_base_addr;

0 commit comments

Comments
 (0)