drivers/char/tpm/tpm-chip.c | 90 +++++++++++++++---------------- drivers/char/tpm/tpm-dev-common.c | 8 +-- drivers/char/tpm/tpm-dev.c | 27 +++++++--- drivers/char/tpm/tpm-dev.h | 1 + drivers/char/tpm/tpm-interface.c | 20 +++++-- drivers/char/tpm/tpm.h | 3 +- drivers/char/tpm/tpm2-space.c | 5 +- drivers/char/tpm/tpm_tis_core.c | 3 +- drivers/char/tpm/tpmrm-dev.c | 20 ++++++- include/linux/tpm.h | 3 +- 10 files changed, 112 insertions(+), 68 deletions(-)
I hit a problem last week were ~ 1% of TPM firmware upgrades were failing. Investigating revealed the issue was that although the upgrade tool uses /dev/tpm0 this does not actually prevent access via /dev/tpmrm0, nor internal kernel users. It *does* prevent access to others via /dev/tpm0 So the upgrade process started, the HW RNG came in to get some randomness in the middle, did the HMAC context dance, and confused everything to the point the TPM was no longer visible to the OS even after a reboot. Thankfully I've been able to recover those devices, but really what I'd like is the ability for a userspace tool to exclusively access the TPM without something coming in behind it. Given the lightweight attempt at locking that already exists I think this was the original intention. As an initial approach I propose this patch set; I don't think the first 2 patches are controversial, but the blocking of kernel access + switch to O_EXCEL in patches 3 + 4 might be. I'm open to alternative suggestions about how to achieve this. (I've sent a separate standalone patch that allows the TPM HW RNG to be disabled at run time, but even with that I think something like this is a good idea as well.) Jonathan McDowell (4): tpm: Ensure exclusive userspace access when using /dev/tpm<n> tpm: Remove tpm_find_get_ops tpm: Allow for exclusive TPM access when using /dev/tpm<n> tpm: Require O_EXCL for exclusive /dev/tpm access drivers/char/tpm/tpm-chip.c | 90 +++++++++++++++---------------- drivers/char/tpm/tpm-dev-common.c | 8 +-- drivers/char/tpm/tpm-dev.c | 27 +++++++--- drivers/char/tpm/tpm-dev.h | 1 + drivers/char/tpm/tpm-interface.c | 20 +++++-- drivers/char/tpm/tpm.h | 3 +- drivers/char/tpm/tpm2-space.c | 5 +- drivers/char/tpm/tpm_tis_core.c | 3 +- drivers/char/tpm/tpmrm-dev.c | 20 ++++++- include/linux/tpm.h | 3 +- 10 files changed, 112 insertions(+), 68 deletions(-) -- 2.51.0
I hit a problem last week were ~ 1% of TPM firmware upgrades were failing. Investigating revealed the issue was that although the upgrade tool uses /dev/tpm0 this does not actually prevent access via /dev/tpmrm0, nor internal kernel users. It *does* prevent access to others via /dev/tpm0 So the upgrade process started, the HW RNG came in to get some randomness in the middle, did the HMAC context dance, and confused everything to the point the TPM was no longer visible to the OS even after a reboot. Thankfully I've been able to recover those devices, but really what I'd like is the ability for a userspace tool to exclusively access the TPM without something coming in behind it. Given the lightweight attempt at locking that already exists I think this was the original intention. As an initial approach I propose this patch set; I don't think the first 2 patches are controversial, but the blocking of kernel access + switch to O_EXCEL in patches 3 + 4 might be. I'm open to alternative suggestions about how to achieve this. (I've sent a separate standalone patch that allows the TPM HW RNG to be disabled at run time, but even with that I think something like this is a good idea as well.) Jonathan McDowell (5): tpm: Ensure exclusive userspace access when using /dev/tpm<n> tpm: Remove tpm_find_get_ops tpm: Allow for exclusive TPM access when using /dev/tpm<n> tpm: Require O_EXCL for exclusive /dev/tpm access hwrng: core - Allow runtime disabling of the HW RNG drivers/char/hw_random/core.c | 9 ++-- drivers/char/tpm/tpm-chip.c | 90 +++++++++++++++---------------- drivers/char/tpm/tpm-dev-common.c | 8 +-- drivers/char/tpm/tpm-dev.c | 27 +++++++--- drivers/char/tpm/tpm-dev.h | 1 + drivers/char/tpm/tpm-interface.c | 20 +++++-- drivers/char/tpm/tpm.h | 3 +- drivers/char/tpm/tpm2-space.c | 5 +- drivers/char/tpm/tpm_tis_core.c | 3 +- drivers/char/tpm/tpmrm-dev.c | 20 ++++++- include/linux/tpm.h | 3 +- 11 files changed, 118 insertions(+), 71 deletions(-) -- 2.51.0
From: Jonathan McDowell <noodles@meta.com>
There is an is_open lock on /dev/tpm<n> that dates back to at least
2013, but it only prevents multiple accesses via *this* interface. It is
perfectly possible for userspace to use /dev/tpmrm<n>, or the kernel to
use the internal interfaces, to access the TPM. For tooling expecting
exclusive access, such as firmware updates, this can cause issues.
Close the userspace loophole by changing the simple bit lock to a full
read/write mutex. Direct /dev/tpm<n> access needs an exclusive write
lock, the resource broker continues to allow concurrent access *except*
when /dev/tpm<n> is open.
Signed-off-by: Jonathan McDowell <noodles@meta.com>
---
v2: Change error path label to err instead of out. Rework commit
message.
drivers/char/tpm/tpm-chip.c | 1 +
drivers/char/tpm/tpm-dev.c | 14 ++++++++------
drivers/char/tpm/tpmrm-dev.c | 20 ++++++++++++++++++--
include/linux/tpm.h | 3 ++-
4 files changed, 29 insertions(+), 9 deletions(-)
diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
index e25daf2396d3..8c8e9054762a 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -338,6 +338,7 @@ struct tpm_chip *tpm_chip_alloc(struct device *pdev,
mutex_init(&chip->tpm_mutex);
init_rwsem(&chip->ops_sem);
+ init_rwsem(&chip->open_lock);
chip->ops = ops;
diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c
index 97c94b5e9340..80c4b3f3ad18 100644
--- a/drivers/char/tpm/tpm-dev.c
+++ b/drivers/char/tpm/tpm-dev.c
@@ -22,10 +22,12 @@ static int tpm_open(struct inode *inode, struct file *file)
chip = container_of(inode->i_cdev, struct tpm_chip, cdev);
- /* It's assured that the chip will be opened just once,
- * by the check of is_open variable, which is protected
- * by driver_lock. */
- if (test_and_set_bit(0, &chip->is_open)) {
+ /*
+ * Only one client is allowed to have /dev/tpm0 open at a time, so we
+ * treat it as a write lock. The shared /dev/tpmrm0 is treated as a
+ * read lock.
+ */
+ if (!down_write_trylock(&chip->open_lock)) {
dev_dbg(&chip->dev, "Another process owns this TPM\n");
return -EBUSY;
}
@@ -39,7 +41,7 @@ static int tpm_open(struct inode *inode, struct file *file)
return 0;
out:
- clear_bit(0, &chip->is_open);
+ up_write(&chip->open_lock);
return -ENOMEM;
}
@@ -51,7 +53,7 @@ static int tpm_release(struct inode *inode, struct file *file)
struct file_priv *priv = file->private_data;
tpm_common_release(file, priv);
- clear_bit(0, &priv->chip->is_open);
+ up_write(&priv->chip->open_lock);
kfree(priv);
return 0;
diff --git a/drivers/char/tpm/tpmrm-dev.c b/drivers/char/tpm/tpmrm-dev.c
index c25df7ea064e..13322dd9ac9e 100644
--- a/drivers/char/tpm/tpmrm-dev.c
+++ b/drivers/char/tpm/tpmrm-dev.c
@@ -17,19 +17,34 @@ static int tpmrm_open(struct inode *inode, struct file *file)
int rc;
chip = container_of(inode->i_cdev, struct tpm_chip, cdevs);
+
+ /*
+ * Only one client is allowed to have /dev/tpm0 open at a time, so we
+ * treat it as a write lock. The shared /dev/tpmrm0 is treated as a
+ * read lock.
+ */
+ if (!down_read_trylock(&chip->open_lock)) {
+ dev_dbg(&chip->dev, "Another process owns this TPM\n");
+ return -EBUSY;
+ }
+
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (priv == NULL)
- return -ENOMEM;
+ goto err;
rc = tpm2_init_space(&priv->space, TPM2_SPACE_BUFFER_SIZE);
if (rc) {
kfree(priv);
- return -ENOMEM;
+ goto err;
}
tpm_common_open(file, chip, &priv->priv, &priv->space);
return 0;
+
+err:
+ up_read(&chip->open_lock);
+ return -ENOMEM;
}
static int tpmrm_release(struct inode *inode, struct file *file)
@@ -40,6 +55,7 @@ static int tpmrm_release(struct inode *inode, struct file *file)
tpm_common_release(file, fpriv);
tpm2_del_space(fpriv->chip, &priv->space);
kfree(priv);
+ up_read(&fpriv->chip->open_lock);
return 0;
}
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index b0e9eb5ef022..548362d20b32 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -22,6 +22,7 @@
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/highmem.h>
+#include <linux/rwsem.h>
#include <crypto/hash_info.h>
#include <crypto/aes.h>
@@ -168,7 +169,7 @@ struct tpm_chip {
unsigned int flags;
int dev_num; /* /dev/tpm# */
- unsigned long is_open; /* only one allowed */
+ struct rw_semaphore open_lock;
char hwrng_name[64];
struct hwrng hwrng;
--
2.51.0
On Tue, Sep 23, 2025 at 06:10:00PM +0100, Jonathan McDowell wrote: > From: Jonathan McDowell <noodles@meta.com> > > There is an is_open lock on /dev/tpm<n> that dates back to at least > 2013, but it only prevents multiple accesses via *this* interface. It is > perfectly possible for userspace to use /dev/tpmrm<n>, or the kernel to > use the internal interfaces, to access the TPM. For tooling expecting > exclusive access, such as firmware updates, this can cause issues. > > Close the userspace loophole by changing the simple bit lock to a full > read/write mutex. Direct /dev/tpm<n> access needs an exclusive write > lock, the resource broker continues to allow concurrent access *except* > when /dev/tpm<n> is open. > > Signed-off-by: Jonathan McDowell <noodles@meta.com> I'm mostly thinking should be tag it as bug fix and backport or not. > --- > v2: Change error path label to err instead of out. Rework commit > message. > > drivers/char/tpm/tpm-chip.c | 1 + > drivers/char/tpm/tpm-dev.c | 14 ++++++++------ > drivers/char/tpm/tpmrm-dev.c | 20 ++++++++++++++++++-- > include/linux/tpm.h | 3 ++- > 4 files changed, 29 insertions(+), 9 deletions(-) > > diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c > index e25daf2396d3..8c8e9054762a 100644 > --- a/drivers/char/tpm/tpm-chip.c > +++ b/drivers/char/tpm/tpm-chip.c > @@ -338,6 +338,7 @@ struct tpm_chip *tpm_chip_alloc(struct device *pdev, > > mutex_init(&chip->tpm_mutex); > init_rwsem(&chip->ops_sem); > + init_rwsem(&chip->open_lock); > > chip->ops = ops; > > diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c > index 97c94b5e9340..80c4b3f3ad18 100644 > --- a/drivers/char/tpm/tpm-dev.c > +++ b/drivers/char/tpm/tpm-dev.c > @@ -22,10 +22,12 @@ static int tpm_open(struct inode *inode, struct file *file) > > chip = container_of(inode->i_cdev, struct tpm_chip, cdev); > > - /* It's assured that the chip will be opened just once, > - * by the check of is_open variable, which is protected > - * by driver_lock. */ > - if (test_and_set_bit(0, &chip->is_open)) { > + /* > + * Only one client is allowed to have /dev/tpm0 open at a time, so we > + * treat it as a write lock. The shared /dev/tpmrm0 is treated as a > + * read lock. > + */ > + if (!down_write_trylock(&chip->open_lock)) { > dev_dbg(&chip->dev, "Another process owns this TPM\n"); > return -EBUSY; > } > @@ -39,7 +41,7 @@ static int tpm_open(struct inode *inode, struct file *file) > return 0; > > out: > - clear_bit(0, &chip->is_open); > + up_write(&chip->open_lock); > return -ENOMEM; > } > > @@ -51,7 +53,7 @@ static int tpm_release(struct inode *inode, struct file *file) > struct file_priv *priv = file->private_data; > > tpm_common_release(file, priv); > - clear_bit(0, &priv->chip->is_open); > + up_write(&priv->chip->open_lock); > kfree(priv); > > return 0; > diff --git a/drivers/char/tpm/tpmrm-dev.c b/drivers/char/tpm/tpmrm-dev.c > index c25df7ea064e..13322dd9ac9e 100644 > --- a/drivers/char/tpm/tpmrm-dev.c > +++ b/drivers/char/tpm/tpmrm-dev.c > @@ -17,19 +17,34 @@ static int tpmrm_open(struct inode *inode, struct file *file) > int rc; > > chip = container_of(inode->i_cdev, struct tpm_chip, cdevs); > + > + /* > + * Only one client is allowed to have /dev/tpm0 open at a time, so we > + * treat it as a write lock. The shared /dev/tpmrm0 is treated as a > + * read lock. > + */ > + if (!down_read_trylock(&chip->open_lock)) { > + dev_dbg(&chip->dev, "Another process owns this TPM\n"); > + return -EBUSY; > + } > + > priv = kzalloc(sizeof(*priv), GFP_KERNEL); > if (priv == NULL) > - return -ENOMEM; > + goto err; > > rc = tpm2_init_space(&priv->space, TPM2_SPACE_BUFFER_SIZE); > if (rc) { > kfree(priv); > - return -ENOMEM; > + goto err; > } > > tpm_common_open(file, chip, &priv->priv, &priv->space); > > return 0; > + > +err: > + up_read(&chip->open_lock); > + return -ENOMEM; > } > > static int tpmrm_release(struct inode *inode, struct file *file) > @@ -40,6 +55,7 @@ static int tpmrm_release(struct inode *inode, struct file *file) > tpm_common_release(file, fpriv); > tpm2_del_space(fpriv->chip, &priv->space); > kfree(priv); > + up_read(&fpriv->chip->open_lock); > > return 0; > } > diff --git a/include/linux/tpm.h b/include/linux/tpm.h > index b0e9eb5ef022..548362d20b32 100644 > --- a/include/linux/tpm.h > +++ b/include/linux/tpm.h > @@ -22,6 +22,7 @@ > #include <linux/cdev.h> > #include <linux/fs.h> > #include <linux/highmem.h> > +#include <linux/rwsem.h> > #include <crypto/hash_info.h> > #include <crypto/aes.h> > > @@ -168,7 +169,7 @@ struct tpm_chip { > unsigned int flags; > > int dev_num; /* /dev/tpm# */ > - unsigned long is_open; /* only one allowed */ > + struct rw_semaphore open_lock; > > char hwrng_name[64]; > struct hwrng hwrng; > -- > 2.51.0 > Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org> BR, Jarkko
From: Jonathan McDowell <noodles@meta.com>
tpm_find_get_ops() looks for the first valid TPM if the caller passes in
NULL. All internal users have been converted to either associate
themselves with a TPM directly, or call tpm_default_chip() as part of
their setup. Remove the no longer necessary tpm_find_get_ops().
Signed-off-by: Jonathan McDowell <noodles@meta.com>
---
drivers/char/tpm/tpm-chip.c | 36 --------------------------------
drivers/char/tpm/tpm-interface.c | 20 ++++++++++++++----
drivers/char/tpm/tpm.h | 1 -
drivers/char/tpm/tpm_tis_core.c | 3 +--
4 files changed, 17 insertions(+), 43 deletions(-)
diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
index 8c8e9054762a..ba906966721a 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -230,42 +230,6 @@ struct tpm_chip *tpm_default_chip(void)
}
EXPORT_SYMBOL_GPL(tpm_default_chip);
-/**
- * tpm_find_get_ops() - find and reserve a TPM chip
- * @chip: a &struct tpm_chip instance, %NULL for the default chip
- *
- * Finds a TPM chip and reserves its class device and operations. The chip must
- * be released with tpm_put_ops() after use.
- * This function is for internal use only. It supports existing TPM callers
- * by accepting NULL, but those callers should be converted to pass in a chip
- * directly.
- *
- * Return:
- * A reserved &struct tpm_chip instance.
- * %NULL if a chip is not found.
- * %NULL if the chip is not available.
- */
-struct tpm_chip *tpm_find_get_ops(struct tpm_chip *chip)
-{
- int rc;
-
- if (chip) {
- if (!tpm_try_get_ops(chip))
- return chip;
- return NULL;
- }
-
- chip = tpm_default_chip();
- if (!chip)
- return NULL;
- rc = tpm_try_get_ops(chip);
- /* release additional reference we got from tpm_default_chip() */
- put_device(&chip->dev);
- if (rc)
- return NULL;
- return chip;
-}
-
/**
* tpm_dev_release() - free chip memory and the device number
* @dev: the character device for the TPM chip
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index b71725827743..8f65dc06a157 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -313,10 +313,13 @@ int tpm_is_tpm2(struct tpm_chip *chip)
{
int rc;
- chip = tpm_find_get_ops(chip);
if (!chip)
return -ENODEV;
+ rc = tpm_try_get_ops(chip);
+ if (rc)
+ return rc;
+
rc = (chip->flags & TPM_CHIP_FLAG_TPM2) != 0;
tpm_put_ops(chip);
@@ -338,10 +341,13 @@ int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
{
int rc;
- chip = tpm_find_get_ops(chip);
if (!chip)
return -ENODEV;
+ rc = tpm_try_get_ops(chip);
+ if (rc)
+ return rc;
+
if (chip->flags & TPM_CHIP_FLAG_TPM2)
rc = tpm2_pcr_read(chip, pcr_idx, digest, NULL);
else
@@ -369,10 +375,13 @@ int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
int rc;
int i;
- chip = tpm_find_get_ops(chip);
if (!chip)
return -ENODEV;
+ rc = tpm_try_get_ops(chip);
+ if (rc)
+ return rc;
+
for (i = 0; i < chip->nr_allocated_banks; i++) {
if (digests[i].alg_id != chip->allocated_banks[i].alg_id) {
rc = -EINVAL;
@@ -492,10 +501,13 @@ int tpm_get_random(struct tpm_chip *chip, u8 *out, size_t max)
if (!out || max > TPM_MAX_RNG_DATA)
return -EINVAL;
- chip = tpm_find_get_ops(chip);
if (!chip)
return -ENODEV;
+ rc = tpm_try_get_ops(chip);
+ if (rc)
+ return rc;
+
if (chip->flags & TPM_CHIP_FLAG_TPM2)
rc = tpm2_get_random(chip, out, max);
else
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 7bb87fa5f7a1..9c158c55c05f 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -267,7 +267,6 @@ static inline void tpm_msleep(unsigned int delay_msec)
int tpm_chip_bootstrap(struct tpm_chip *chip);
int tpm_chip_start(struct tpm_chip *chip);
void tpm_chip_stop(struct tpm_chip *chip);
-struct tpm_chip *tpm_find_get_ops(struct tpm_chip *chip);
struct tpm_chip *tpm_chip_alloc(struct device *dev,
const struct tpm_class_ops *ops);
diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c
index 4b12c4b9da8b..73b94f4daf4b 100644
--- a/drivers/char/tpm/tpm_tis_core.c
+++ b/drivers/char/tpm/tpm_tis_core.c
@@ -265,8 +265,7 @@ static u8 tpm_tis_status(struct tpm_chip *chip)
/*
* Dump stack for forensics, as invalid TPM_STS.x could be
- * potentially triggered by impaired tpm_try_get_ops() or
- * tpm_find_get_ops().
+ * potentially triggered by impaired tpm_try_get_ops().
*/
dump_stack();
}
--
2.51.0
On Tue, Sep 23, 2025 at 06:10:11PM +0100, Jonathan McDowell wrote: > From: Jonathan McDowell <noodles@meta.com> > > tpm_find_get_ops() looks for the first valid TPM if the caller passes in > NULL. All internal users have been converted to either associate > themselves with a TPM directly, or call tpm_default_chip() as part of > their setup. Remove the no longer necessary tpm_find_get_ops(). > > Signed-off-by: Jonathan McDowell <noodles@meta.com> This patch fits really well with the changes I've been working on streamlining some parts (mainly tpm_bufs and sanitizing tpm2-sessions). I might have ended up doing this type of path myself so I'm glad it's done. > --- > drivers/char/tpm/tpm-chip.c | 36 -------------------------------- > drivers/char/tpm/tpm-interface.c | 20 ++++++++++++++---- > drivers/char/tpm/tpm.h | 1 - > drivers/char/tpm/tpm_tis_core.c | 3 +-- > 4 files changed, 17 insertions(+), 43 deletions(-) > > diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c > index 8c8e9054762a..ba906966721a 100644 > --- a/drivers/char/tpm/tpm-chip.c > +++ b/drivers/char/tpm/tpm-chip.c > @@ -230,42 +230,6 @@ struct tpm_chip *tpm_default_chip(void) > } > EXPORT_SYMBOL_GPL(tpm_default_chip); > > -/** > - * tpm_find_get_ops() - find and reserve a TPM chip > - * @chip: a &struct tpm_chip instance, %NULL for the default chip > - * > - * Finds a TPM chip and reserves its class device and operations. The chip must > - * be released with tpm_put_ops() after use. > - * This function is for internal use only. It supports existing TPM callers > - * by accepting NULL, but those callers should be converted to pass in a chip > - * directly. > - * > - * Return: > - * A reserved &struct tpm_chip instance. > - * %NULL if a chip is not found. > - * %NULL if the chip is not available. > - */ > -struct tpm_chip *tpm_find_get_ops(struct tpm_chip *chip) > -{ > - int rc; > - > - if (chip) { > - if (!tpm_try_get_ops(chip)) > - return chip; > - return NULL; > - } > - > - chip = tpm_default_chip(); > - if (!chip) > - return NULL; > - rc = tpm_try_get_ops(chip); > - /* release additional reference we got from tpm_default_chip() */ > - put_device(&chip->dev); > - if (rc) > - return NULL; > - return chip; > -} > - > /** > * tpm_dev_release() - free chip memory and the device number > * @dev: the character device for the TPM chip > diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c > index b71725827743..8f65dc06a157 100644 > --- a/drivers/char/tpm/tpm-interface.c > +++ b/drivers/char/tpm/tpm-interface.c > @@ -313,10 +313,13 @@ int tpm_is_tpm2(struct tpm_chip *chip) > { > int rc; > > - chip = tpm_find_get_ops(chip); > if (!chip) > return -ENODEV; > > + rc = tpm_try_get_ops(chip); > + if (rc) > + return rc; > + > rc = (chip->flags & TPM_CHIP_FLAG_TPM2) != 0; > > tpm_put_ops(chip); > @@ -338,10 +341,13 @@ int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx, > { > int rc; > > - chip = tpm_find_get_ops(chip); > if (!chip) > return -ENODEV; > > + rc = tpm_try_get_ops(chip); > + if (rc) > + return rc; > + > if (chip->flags & TPM_CHIP_FLAG_TPM2) > rc = tpm2_pcr_read(chip, pcr_idx, digest, NULL); > else > @@ -369,10 +375,13 @@ int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, > int rc; > int i; > > - chip = tpm_find_get_ops(chip); > if (!chip) > return -ENODEV; > > + rc = tpm_try_get_ops(chip); > + if (rc) > + return rc; > + > for (i = 0; i < chip->nr_allocated_banks; i++) { > if (digests[i].alg_id != chip->allocated_banks[i].alg_id) { > rc = -EINVAL; > @@ -492,10 +501,13 @@ int tpm_get_random(struct tpm_chip *chip, u8 *out, size_t max) > if (!out || max > TPM_MAX_RNG_DATA) > return -EINVAL; > > - chip = tpm_find_get_ops(chip); > if (!chip) > return -ENODEV; > > + rc = tpm_try_get_ops(chip); > + if (rc) > + return rc; > + > if (chip->flags & TPM_CHIP_FLAG_TPM2) > rc = tpm2_get_random(chip, out, max); > else > diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h > index 7bb87fa5f7a1..9c158c55c05f 100644 > --- a/drivers/char/tpm/tpm.h > +++ b/drivers/char/tpm/tpm.h > @@ -267,7 +267,6 @@ static inline void tpm_msleep(unsigned int delay_msec) > int tpm_chip_bootstrap(struct tpm_chip *chip); > int tpm_chip_start(struct tpm_chip *chip); > void tpm_chip_stop(struct tpm_chip *chip); > -struct tpm_chip *tpm_find_get_ops(struct tpm_chip *chip); > > struct tpm_chip *tpm_chip_alloc(struct device *dev, > const struct tpm_class_ops *ops); > diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c > index 4b12c4b9da8b..73b94f4daf4b 100644 > --- a/drivers/char/tpm/tpm_tis_core.c > +++ b/drivers/char/tpm/tpm_tis_core.c > @@ -265,8 +265,7 @@ static u8 tpm_tis_status(struct tpm_chip *chip) > > /* > * Dump stack for forensics, as invalid TPM_STS.x could be > - * potentially triggered by impaired tpm_try_get_ops() or > - * tpm_find_get_ops(). > + * potentially triggered by impaired tpm_try_get_ops(). > */ > dump_stack(); > } > -- > 2.51.0 > Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org> BR, Jarkko
From: Jonathan McDowell <noodles@meta.com>
There are situations where userspace might reasonably desire exclusive
access to the TPM, or the kernel's internal context saving + flushing
may cause issues, for example when performing firmware upgrades. Extend
the locking already used for avoiding concurrent userspace access to
prevent internal users of the TPM when /dev/tpm<n> is in use.
The few internal users who already hold the open_lock are changed to use
tpm_(try_get|put)_ops_locked, with the old tpm_(try_get|put)_ops
functions changing to obtain read access to the open_lock. We return
-EBUSY when another user has exclusive access, rather than adding waits.
Signed-off-by: Jonathan McDowell <noodles@meta.com>
---
v2: Switch to _locked instead of _internal_ for function names.
drivers/char/tpm/tpm-chip.c | 53 +++++++++++++++++++++++++------
drivers/char/tpm/tpm-dev-common.c | 8 ++---
drivers/char/tpm/tpm.h | 2 ++
drivers/char/tpm/tpm2-space.c | 5 ++-
4 files changed, 52 insertions(+), 16 deletions(-)
diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
index ba906966721a..687f6d8cd601 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -144,7 +144,7 @@ void tpm_chip_stop(struct tpm_chip *chip)
EXPORT_SYMBOL_GPL(tpm_chip_stop);
/**
- * tpm_try_get_ops() - Get a ref to the tpm_chip
+ * tpm_try_get_ops_locked() - Get a ref to the tpm_chip
* @chip: Chip to ref
*
* The caller must already have some kind of locking to ensure that chip is
@@ -154,7 +154,7 @@ EXPORT_SYMBOL_GPL(tpm_chip_stop);
*
* Returns -ERRNO if the chip could not be got.
*/
-int tpm_try_get_ops(struct tpm_chip *chip)
+int tpm_try_get_ops_locked(struct tpm_chip *chip)
{
int rc = -EIO;
@@ -185,22 +185,57 @@ int tpm_try_get_ops(struct tpm_chip *chip)
put_device(&chip->dev);
return rc;
}
-EXPORT_SYMBOL_GPL(tpm_try_get_ops);
/**
- * tpm_put_ops() - Release a ref to the tpm_chip
+ * tpm_put_ops_locked() - Release a ref to the tpm_chip
* @chip: Chip to put
*
- * This is the opposite pair to tpm_try_get_ops(). After this returns chip may
- * be kfree'd.
+ * This is the opposite pair to tpm_try_get_ops_locked(). After this returns
+ * chip may be kfree'd.
*/
-void tpm_put_ops(struct tpm_chip *chip)
+void tpm_put_ops_locked(struct tpm_chip *chip)
{
tpm_chip_stop(chip);
mutex_unlock(&chip->tpm_mutex);
up_read(&chip->ops_sem);
put_device(&chip->dev);
}
+
+/**
+ * tpm_try_get_ops() - Get a ref to the tpm_chip
+ * @chip: Chip to ref
+ *
+ * The caller must already have some kind of locking to ensure that chip is
+ * valid. This function will attempt to get the open_lock for the chip,
+ * ensuring no other user is expecting exclusive access, before locking the
+ * chip so that the ops member can be accessed safely. The locking prevents
+ * tpm_chip_unregister from completing, so it should not be held for long
+ * periods.
+ *
+ * Returns -ERRNO if the chip could not be got.
+ */
+int tpm_try_get_ops(struct tpm_chip *chip)
+{
+ if (!down_read_trylock(&chip->open_lock))
+ return -EBUSY;
+
+ return tpm_try_get_ops_locked(chip);
+}
+EXPORT_SYMBOL_GPL(tpm_try_get_ops);
+
+/**
+ * tpm_put_ops() - Release a ref to the tpm_chip
+ * @chip: Chip to put
+ *
+ * This is the opposite pair to tpm_try_get_ops(). After this returns
+ * chip may be kfree'd.
+ */
+void tpm_put_ops(struct tpm_chip *chip)
+{
+ tpm_put_ops_locked(chip);
+
+ up_read(&chip->open_lock);
+}
EXPORT_SYMBOL_GPL(tpm_put_ops);
/**
@@ -644,10 +679,10 @@ void tpm_chip_unregister(struct tpm_chip *chip)
#ifdef CONFIG_TCG_TPM2_HMAC
int rc;
- rc = tpm_try_get_ops(chip);
+ rc = tpm_try_get_ops_locked(chip);
if (!rc) {
tpm2_end_auth_session(chip);
- tpm_put_ops(chip);
+ tpm_put_ops_locked(chip);
}
#endif
diff --git a/drivers/char/tpm/tpm-dev-common.c b/drivers/char/tpm/tpm-dev-common.c
index f2a5e09257dd..0f5bc63411aa 100644
--- a/drivers/char/tpm/tpm-dev-common.c
+++ b/drivers/char/tpm/tpm-dev-common.c
@@ -65,7 +65,7 @@ static void tpm_dev_async_work(struct work_struct *work)
mutex_lock(&priv->buffer_mutex);
priv->command_enqueued = false;
- ret = tpm_try_get_ops(priv->chip);
+ ret = tpm_try_get_ops_locked(priv->chip);
if (ret) {
priv->response_length = ret;
goto out;
@@ -73,7 +73,7 @@ static void tpm_dev_async_work(struct work_struct *work)
ret = tpm_dev_transmit(priv->chip, priv->space, priv->data_buffer,
sizeof(priv->data_buffer));
- tpm_put_ops(priv->chip);
+ tpm_put_ops_locked(priv->chip);
/*
* If ret is > 0 then tpm_dev_transmit returned the size of the
@@ -220,14 +220,14 @@ ssize_t tpm_common_write(struct file *file, const char __user *buf,
* lock during this period so that the tpm can be unregistered even if
* the char dev is held open.
*/
- if (tpm_try_get_ops(priv->chip)) {
+ if (tpm_try_get_ops_locked(priv->chip)) {
ret = -EPIPE;
goto out;
}
ret = tpm_dev_transmit(priv->chip, priv->space, priv->data_buffer,
sizeof(priv->data_buffer));
- tpm_put_ops(priv->chip);
+ tpm_put_ops_locked(priv->chip);
if (ret > 0) {
priv->response_length = ret;
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 9c158c55c05f..c2ec56e2cd24 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -272,6 +272,8 @@ struct tpm_chip *tpm_chip_alloc(struct device *dev,
const struct tpm_class_ops *ops);
struct tpm_chip *tpmm_chip_alloc(struct device *pdev,
const struct tpm_class_ops *ops);
+int tpm_try_get_ops_locked(struct tpm_chip *chip);
+void tpm_put_ops_locked(struct tpm_chip *chip);
int tpm_chip_register(struct tpm_chip *chip);
void tpm_chip_unregister(struct tpm_chip *chip);
diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c
index 60354cd53b5c..0ad5e18355e0 100644
--- a/drivers/char/tpm/tpm2-space.c
+++ b/drivers/char/tpm/tpm2-space.c
@@ -58,10 +58,9 @@ int tpm2_init_space(struct tpm_space *space, unsigned int buf_size)
void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space)
{
-
- if (tpm_try_get_ops(chip) == 0) {
+ if (tpm_try_get_ops_locked(chip) == 0) {
tpm2_flush_sessions(chip, space);
- tpm_put_ops(chip);
+ tpm_put_ops_locked(chip);
}
kfree(space->context_buf);
--
2.51.0
On Tue, Sep 23, 2025 at 06:10:19PM +0100, Jonathan McDowell wrote: > From: Jonathan McDowell <noodles@meta.com> > > There are situations where userspace might reasonably desire exclusive > access to the TPM, or the kernel's internal context saving + flushing > may cause issues, for example when performing firmware upgrades. Extend > the locking already used for avoiding concurrent userspace access to > prevent internal users of the TPM when /dev/tpm<n> is in use. > > The few internal users who already hold the open_lock are changed to use > tpm_(try_get|put)_ops_locked, with the old tpm_(try_get|put)_ops > functions changing to obtain read access to the open_lock. We return > -EBUSY when another user has exclusive access, rather than adding waits. > > Signed-off-by: Jonathan McDowell <noodles@meta.com> Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org> > --- > v2: Switch to _locked instead of _internal_ for function names. > > drivers/char/tpm/tpm-chip.c | 53 +++++++++++++++++++++++++------ > drivers/char/tpm/tpm-dev-common.c | 8 ++--- > drivers/char/tpm/tpm.h | 2 ++ > drivers/char/tpm/tpm2-space.c | 5 ++- > 4 files changed, 52 insertions(+), 16 deletions(-) > > diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c > index ba906966721a..687f6d8cd601 100644 > --- a/drivers/char/tpm/tpm-chip.c > +++ b/drivers/char/tpm/tpm-chip.c > @@ -144,7 +144,7 @@ void tpm_chip_stop(struct tpm_chip *chip) > EXPORT_SYMBOL_GPL(tpm_chip_stop); > > /** > - * tpm_try_get_ops() - Get a ref to the tpm_chip > + * tpm_try_get_ops_locked() - Get a ref to the tpm_chip > * @chip: Chip to ref > * > * The caller must already have some kind of locking to ensure that chip is > @@ -154,7 +154,7 @@ EXPORT_SYMBOL_GPL(tpm_chip_stop); > * > * Returns -ERRNO if the chip could not be got. > */ > -int tpm_try_get_ops(struct tpm_chip *chip) > +int tpm_try_get_ops_locked(struct tpm_chip *chip) > { > int rc = -EIO; > > @@ -185,22 +185,57 @@ int tpm_try_get_ops(struct tpm_chip *chip) > put_device(&chip->dev); > return rc; > } > -EXPORT_SYMBOL_GPL(tpm_try_get_ops); > > /** > - * tpm_put_ops() - Release a ref to the tpm_chip > + * tpm_put_ops_locked() - Release a ref to the tpm_chip > * @chip: Chip to put > * > - * This is the opposite pair to tpm_try_get_ops(). After this returns chip may > - * be kfree'd. > + * This is the opposite pair to tpm_try_get_ops_locked(). After this returns > + * chip may be kfree'd. > */ > -void tpm_put_ops(struct tpm_chip *chip) > +void tpm_put_ops_locked(struct tpm_chip *chip) > { > tpm_chip_stop(chip); > mutex_unlock(&chip->tpm_mutex); > up_read(&chip->ops_sem); > put_device(&chip->dev); > } > + > +/** > + * tpm_try_get_ops() - Get a ref to the tpm_chip > + * @chip: Chip to ref > + * > + * The caller must already have some kind of locking to ensure that chip is > + * valid. This function will attempt to get the open_lock for the chip, > + * ensuring no other user is expecting exclusive access, before locking the > + * chip so that the ops member can be accessed safely. The locking prevents > + * tpm_chip_unregister from completing, so it should not be held for long > + * periods. > + * > + * Returns -ERRNO if the chip could not be got. > + */ > +int tpm_try_get_ops(struct tpm_chip *chip) > +{ > + if (!down_read_trylock(&chip->open_lock)) > + return -EBUSY; > + > + return tpm_try_get_ops_locked(chip); > +} > +EXPORT_SYMBOL_GPL(tpm_try_get_ops); > + > +/** > + * tpm_put_ops() - Release a ref to the tpm_chip > + * @chip: Chip to put > + * > + * This is the opposite pair to tpm_try_get_ops(). After this returns > + * chip may be kfree'd. > + */ > +void tpm_put_ops(struct tpm_chip *chip) > +{ > + tpm_put_ops_locked(chip); > + > + up_read(&chip->open_lock); > +} > EXPORT_SYMBOL_GPL(tpm_put_ops); > > /** > @@ -644,10 +679,10 @@ void tpm_chip_unregister(struct tpm_chip *chip) > #ifdef CONFIG_TCG_TPM2_HMAC > int rc; > > - rc = tpm_try_get_ops(chip); > + rc = tpm_try_get_ops_locked(chip); > if (!rc) { > tpm2_end_auth_session(chip); > - tpm_put_ops(chip); > + tpm_put_ops_locked(chip); > } > #endif > > diff --git a/drivers/char/tpm/tpm-dev-common.c b/drivers/char/tpm/tpm-dev-common.c > index f2a5e09257dd..0f5bc63411aa 100644 > --- a/drivers/char/tpm/tpm-dev-common.c > +++ b/drivers/char/tpm/tpm-dev-common.c > @@ -65,7 +65,7 @@ static void tpm_dev_async_work(struct work_struct *work) > > mutex_lock(&priv->buffer_mutex); > priv->command_enqueued = false; > - ret = tpm_try_get_ops(priv->chip); > + ret = tpm_try_get_ops_locked(priv->chip); > if (ret) { > priv->response_length = ret; > goto out; > @@ -73,7 +73,7 @@ static void tpm_dev_async_work(struct work_struct *work) > > ret = tpm_dev_transmit(priv->chip, priv->space, priv->data_buffer, > sizeof(priv->data_buffer)); > - tpm_put_ops(priv->chip); > + tpm_put_ops_locked(priv->chip); > > /* > * If ret is > 0 then tpm_dev_transmit returned the size of the > @@ -220,14 +220,14 @@ ssize_t tpm_common_write(struct file *file, const char __user *buf, > * lock during this period so that the tpm can be unregistered even if > * the char dev is held open. > */ > - if (tpm_try_get_ops(priv->chip)) { > + if (tpm_try_get_ops_locked(priv->chip)) { > ret = -EPIPE; > goto out; > } > > ret = tpm_dev_transmit(priv->chip, priv->space, priv->data_buffer, > sizeof(priv->data_buffer)); > - tpm_put_ops(priv->chip); > + tpm_put_ops_locked(priv->chip); > > if (ret > 0) { > priv->response_length = ret; > diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h > index 9c158c55c05f..c2ec56e2cd24 100644 > --- a/drivers/char/tpm/tpm.h > +++ b/drivers/char/tpm/tpm.h > @@ -272,6 +272,8 @@ struct tpm_chip *tpm_chip_alloc(struct device *dev, > const struct tpm_class_ops *ops); > struct tpm_chip *tpmm_chip_alloc(struct device *pdev, > const struct tpm_class_ops *ops); > +int tpm_try_get_ops_locked(struct tpm_chip *chip); > +void tpm_put_ops_locked(struct tpm_chip *chip); > int tpm_chip_register(struct tpm_chip *chip); > void tpm_chip_unregister(struct tpm_chip *chip); > > diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c > index 60354cd53b5c..0ad5e18355e0 100644 > --- a/drivers/char/tpm/tpm2-space.c > +++ b/drivers/char/tpm/tpm2-space.c > @@ -58,10 +58,9 @@ int tpm2_init_space(struct tpm_space *space, unsigned int buf_size) > > void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space) > { > - > - if (tpm_try_get_ops(chip) == 0) { > + if (tpm_try_get_ops_locked(chip) == 0) { > tpm2_flush_sessions(chip, space); > - tpm_put_ops(chip); > + tpm_put_ops_locked(chip); > } > > kfree(space->context_buf); > -- > 2.51.0 > I can take these to the next PR, thanks. BR, Jarkko
From: Jonathan McDowell <noodles@meta.com>
Given that /dev/tpm has not had exclusive access to the TPM since the
existence of the kernel resource broker and other internal users, stop
defaulted to exclusive access to the first client that opens the device.
Continue to support exclusive access, but only with the use of the
O_EXCL flag on device open.
Signed-off-by: Jonathan McDowell <noodles@meta.com>
---
drivers/char/tpm/tpm-dev.c | 25 +++++++++++++++++++------
drivers/char/tpm/tpm-dev.h | 1 +
2 files changed, 20 insertions(+), 6 deletions(-)
diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c
index 80c4b3f3ad18..8921bbb541c1 100644
--- a/drivers/char/tpm/tpm-dev.c
+++ b/drivers/char/tpm/tpm-dev.c
@@ -19,15 +19,21 @@ static int tpm_open(struct inode *inode, struct file *file)
{
struct tpm_chip *chip;
struct file_priv *priv;
+ int rc;
chip = container_of(inode->i_cdev, struct tpm_chip, cdev);
/*
- * Only one client is allowed to have /dev/tpm0 open at a time, so we
- * treat it as a write lock. The shared /dev/tpmrm0 is treated as a
- * read lock.
+ * If a client uses the O_EXCL flag then it expects to be the only TPM
+ * user, so we treat it as a write lock. Otherwise we do as /dev/tpmrm
+ * and use a read lock.
*/
- if (!down_write_trylock(&chip->open_lock)) {
+ if (file->f_flags & O_EXCL)
+ rc = down_write_trylock(&chip->open_lock);
+ else
+ rc = down_read_trylock(&chip->open_lock);
+
+ if (!rc) {
dev_dbg(&chip->dev, "Another process owns this TPM\n");
return -EBUSY;
}
@@ -35,13 +41,17 @@ static int tpm_open(struct inode *inode, struct file *file)
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (priv == NULL)
goto out;
+ priv->exclusive = (file->f_flags & O_EXCL);
tpm_common_open(file, chip, priv, NULL);
return 0;
out:
- up_write(&chip->open_lock);
+ if (file->f_flags & O_EXCL)
+ up_write(&chip->open_lock);
+ else
+ up_read(&chip->open_lock);
return -ENOMEM;
}
@@ -53,7 +63,10 @@ static int tpm_release(struct inode *inode, struct file *file)
struct file_priv *priv = file->private_data;
tpm_common_release(file, priv);
- up_write(&priv->chip->open_lock);
+ if (priv->exclusive)
+ up_write(&priv->chip->open_lock);
+ else
+ up_read(&priv->chip->open_lock);
kfree(priv);
return 0;
diff --git a/drivers/char/tpm/tpm-dev.h b/drivers/char/tpm/tpm-dev.h
index f3742bcc73e3..0ad8504c73e4 100644
--- a/drivers/char/tpm/tpm-dev.h
+++ b/drivers/char/tpm/tpm-dev.h
@@ -17,6 +17,7 @@ struct file_priv {
ssize_t response_length;
bool response_read;
bool command_enqueued;
+ bool exclusive;
u8 data_buffer[TPM_BUFSIZE];
};
--
2.51.0
On Tue, Sep 23, 2025 at 06:10:28PM +0100, Jonathan McDowell wrote: > From: Jonathan McDowell <noodles@meta.com> > > Given that /dev/tpm has not had exclusive access to the TPM since the > existence of the kernel resource broker and other internal users, stop > defaulted to exclusive access to the first client that opens the device. > Continue to support exclusive access, but only with the use of the > O_EXCL flag on device open. > > Signed-off-by: Jonathan McDowell <noodles@meta.com> > --- > drivers/char/tpm/tpm-dev.c | 25 +++++++++++++++++++------ > drivers/char/tpm/tpm-dev.h | 1 + > 2 files changed, 20 insertions(+), 6 deletions(-) > > diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c > index 80c4b3f3ad18..8921bbb541c1 100644 > --- a/drivers/char/tpm/tpm-dev.c > +++ b/drivers/char/tpm/tpm-dev.c > @@ -19,15 +19,21 @@ static int tpm_open(struct inode *inode, struct file *file) > { > struct tpm_chip *chip; > struct file_priv *priv; > + int rc; > > chip = container_of(inode->i_cdev, struct tpm_chip, cdev); > > /* > - * Only one client is allowed to have /dev/tpm0 open at a time, so we > - * treat it as a write lock. The shared /dev/tpmrm0 is treated as a > - * read lock. > + * If a client uses the O_EXCL flag then it expects to be the only TPM > + * user, so we treat it as a write lock. Otherwise we do as /dev/tpmrm > + * and use a read lock. > */ > - if (!down_write_trylock(&chip->open_lock)) { > + if (file->f_flags & O_EXCL) > + rc = down_write_trylock(&chip->open_lock); > + else > + rc = down_read_trylock(&chip->open_lock); > + > + if (!rc) { > dev_dbg(&chip->dev, "Another process owns this TPM\n"); > return -EBUSY; > } > @@ -35,13 +41,17 @@ static int tpm_open(struct inode *inode, struct file *file) > priv = kzalloc(sizeof(*priv), GFP_KERNEL); > if (priv == NULL) > goto out; > + priv->exclusive = (file->f_flags & O_EXCL); > > tpm_common_open(file, chip, priv, NULL); > > return 0; > > out: > - up_write(&chip->open_lock); > + if (file->f_flags & O_EXCL) > + up_write(&chip->open_lock); > + else > + up_read(&chip->open_lock); > return -ENOMEM; > } > > @@ -53,7 +63,10 @@ static int tpm_release(struct inode *inode, struct file *file) > struct file_priv *priv = file->private_data; > > tpm_common_release(file, priv); > - up_write(&priv->chip->open_lock); > + if (priv->exclusive) > + up_write(&priv->chip->open_lock); > + else > + up_read(&priv->chip->open_lock); > kfree(priv); > > return 0; > diff --git a/drivers/char/tpm/tpm-dev.h b/drivers/char/tpm/tpm-dev.h > index f3742bcc73e3..0ad8504c73e4 100644 > --- a/drivers/char/tpm/tpm-dev.h > +++ b/drivers/char/tpm/tpm-dev.h > @@ -17,6 +17,7 @@ struct file_priv { > ssize_t response_length; > bool response_read; > bool command_enqueued; > + bool exclusive; > > u8 data_buffer[TPM_BUFSIZE]; > }; > -- > 2.51.0 > Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org> BR, Jarkko
© 2016 - 2025 Red Hat, Inc.