Skip to content

Commit 7624c3a

Browse files
tpm, tpm_tis: Handle interrupt storm
Detect an interrupt strom by counting the number of unhandled interrupts within a 10 ms time interval. In case that more than 1000 were unhandled deactivate interrupts and fall back to polling. Use a worker as helper to trigger interrupt deactivation from within the irq handler. Suggested-by: Lukas Wunner <lukas@wunner.de> Signed-off-by: Lino Sanfilippo <l.sanfilippo@kunbus.com>
1 parent c8b54bc commit 7624c3a

2 files changed

Lines changed: 49 additions & 3 deletions

File tree

drivers/char/tpm/tpm_tis_core.c

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -755,17 +755,18 @@ static bool tpm_tis_req_canceled(struct tpm_chip *chip, u8 status)
755755

756756
static irqreturn_t tis_int_handler(int dummy, void *dev_id)
757757
{
758+
const unsigned int MAX_UNHANDLED_IRQS = 1000;
758759
struct tpm_chip *chip = dev_id;
759760
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
760761
u32 interrupt;
761762
int rc;
762763

763764
rc = tpm_tis_read32(priv, TPM_INT_STATUS(priv->locality), &interrupt);
764765
if (rc < 0)
765-
return IRQ_NONE;
766+
goto unhandled;
766767

767768
if (interrupt == 0)
768-
return IRQ_NONE;
769+
goto unhandled;
769770

770771
set_bit(TPM_TIS_IRQ_TESTED, &priv->flags);
771772
if (interrupt & TPM_INTF_DATA_AVAIL_INT)
@@ -781,10 +782,37 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id)
781782
rc = tpm_tis_write32(priv, TPM_INT_STATUS(priv->locality), interrupt);
782783
tpm_tis_relinquish_locality(chip, 0);
783784
if (rc < 0)
784-
return IRQ_NONE;
785+
goto unhandled;
785786

786787
tpm_tis_read32(priv, TPM_INT_STATUS(priv->locality), &interrupt);
787788
return IRQ_HANDLED;
789+
790+
unhandled:
791+
if (time_after(jiffies, priv->last_unhandled_irq + HZ/10))
792+
priv->unhandled_irqs = 1;
793+
else
794+
priv->unhandled_irqs++;
795+
796+
priv->last_unhandled_irq = jiffies;
797+
798+
if ((priv->unhandled_irqs > MAX_UNHANDLED_IRQS) &&
799+
(chip->flags & TPM_CHIP_FLAG_IRQ)) {
800+
int intmask;
801+
802+
tpm_tis_request_locality(chip, 0);
803+
rc = tpm_tis_read32(priv, TPM_INT_ENABLE(priv->locality), &intmask);
804+
if (rc < 0)
805+
intmask = 0;
806+
807+
intmask &= ~TPM_GLOBAL_INT_ENABLE;
808+
rc = tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask);
809+
tpm_tis_relinquish_locality(chip, 0);
810+
811+
chip->flags &= ~TPM_CHIP_FLAG_IRQ;
812+
813+
schedule_work(&priv->free_irq_work);
814+
}
815+
return IRQ_HANDLED;
788816
}
789817

790818
static void tpm_tis_gen_interrupt(struct tpm_chip *chip)
@@ -805,6 +833,15 @@ static void tpm_tis_gen_interrupt(struct tpm_chip *chip)
805833
chip->flags &= ~TPM_CHIP_FLAG_IRQ;
806834
}
807835

836+
static void tpm_tis_free_irq_func(struct work_struct *work)
837+
{
838+
struct tpm_tis_data *priv = container_of(work, typeof(*priv), free_irq_work);
839+
struct tpm_chip *chip = priv->chip;
840+
841+
devm_free_irq(chip->dev.parent, priv->irq, chip);
842+
priv->irq = 0;
843+
}
844+
808845
/* Register the IRQ and issue a command that will cause an interrupt. If an
809846
* irq is seen then leave the chip setup for IRQ operation, otherwise reverse
810847
* everything and leave in polling mode. Returns 0 on success.
@@ -817,6 +854,7 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask,
817854
int rc;
818855
u32 int_status;
819856

857+
INIT_WORK(&priv->free_irq_work, tpm_tis_free_irq_func);
820858

821859
rc = devm_request_threaded_irq(chip->dev.parent, irq, NULL,
822860
tis_int_handler, IRQF_ONESHOT | flags,
@@ -919,6 +957,7 @@ void tpm_tis_remove(struct tpm_chip *chip)
919957
interrupt = 0;
920958

921959
tpm_tis_write32(priv, reg, ~TPM_GLOBAL_INT_ENABLE & interrupt);
960+
flush_work(&priv->free_irq_work);
922961

923962
tpm_tis_clkrun_enable(chip, false);
924963

@@ -1023,6 +1062,7 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
10231062
chip->timeout_b = msecs_to_jiffies(TIS_TIMEOUT_B_MAX);
10241063
chip->timeout_c = msecs_to_jiffies(TIS_TIMEOUT_C_MAX);
10251064
chip->timeout_d = msecs_to_jiffies(TIS_TIMEOUT_D_MAX);
1065+
priv->chip = chip;
10261066
priv->timeout_min = TPM_TIMEOUT_USECS_MIN;
10271067
priv->timeout_max = TPM_TIMEOUT_USECS_MAX;
10281068
priv->phy_ops = phy_ops;

drivers/char/tpm/tpm_tis_core.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,18 @@ 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;
99+
/* Interrupts */
98100
int irq;
101+
struct work_struct free_irq_work;
102+
unsigned long last_unhandled_irq;
103+
unsigned int unhandled_irqs;
99104
unsigned int int_mask;
105+
100106
unsigned long flags;
101107
void __iomem *ilb_base_addr;
102108
u16 clkrun_enabled;

0 commit comments

Comments
 (0)