[PATCH] uio:uio_pci_generic:Don't clear master bit when the process does not exit

From: Su Weifeng
Date: Tue Feb 14 2023 - 07:57:36 EST


From: Weifeng Su <suweifeng1@xxxxxxxxxx>

The /dev/uioX device is used by multiple processes. The current behavior
is to clear the master bit when a process exits. This affects other
processes that use the device, resulting in command suspension and
timeout. This behavior cannot be sensed by the process itself.
The solution is to add the reference counting. The reference count is
self-incremented and self-decremented each time when the device open and
close. The master bit is cleared only when the last process exited.

Signed-off-by: Weifeng Su <suweifeng1@xxxxxxxxxx>
---
drivers/uio/uio_pci_generic.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/drivers/uio/uio_pci_generic.c b/drivers/uio/uio_pci_generic.c
index e03f9b532..d36d3e08e 100644
--- a/drivers/uio/uio_pci_generic.c
+++ b/drivers/uio/uio_pci_generic.c
@@ -31,6 +31,7 @@
struct uio_pci_generic_dev {
struct uio_info info;
struct pci_dev *pdev;
+ refcount_t dev_refc;
};

static inline struct uio_pci_generic_dev *
@@ -39,10 +40,22 @@ to_uio_pci_generic_dev(struct uio_info *info)
return container_of(info, struct uio_pci_generic_dev, info);
}

+static int open(struct uio_info *info, struct inode *inode)
+{
+ struct uio_pci_generic_dev *gdev = to_uio_pci_generic_dev(info);
+
+ if (gdev)
+ refcount_inc(&gdev->dev_refc);
+ return 0;
+}
+
static int release(struct uio_info *info, struct inode *inode)
{
struct uio_pci_generic_dev *gdev = to_uio_pci_generic_dev(info);

+ if (gdev && refcount_dec_not_one(&gdev->dev_refc))
+ return 0;
+
/*
* This driver is insecure when used with devices doing DMA, but some
 * people (mis)use it with such devices.
@@ -51,7 +64,8 @@ static int release(struct uio_info *info, struct inode *inode)
 * Note that there's a non-zero chance doing this will wedge the device
 * at least until reset.
*/
- pci_clear_master(gdev->pdev);
+ if (refcount_read(&gdev->dev_refc) == 1)
+ pci_clear_master(gdev->pdev);
return 0;
}

@@ -93,7 +107,9 @@ static int probe(struct pci_dev *pdev,
gdev->info.name = "uio_pci_generic";
gdev->info.version = DRIVER_VERSION;
gdev->info.release = release;
+ gdev->info.open = open;
gdev->pdev = pdev;
+ refcount_set(&gdev->dev_refc, 1);
if (pdev->irq && (pdev->irq != IRQ_NOTCONNECTED)) {
gdev->info.irq = pdev->irq;
gdev->info.irq_flags = IRQF_SHARED;
--
2.33.0