Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F159335860
D57429.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
5 KB
Referenced Files
None
Subscribers
None
D57429.id.diff
View Options
diff --git a/sys/compat/linuxkpi/common/src/linux_pci.c b/sys/compat/linuxkpi/common/src/linux_pci.c
--- a/sys/compat/linuxkpi/common/src/linux_pci.c
+++ b/sys/compat/linuxkpi/common/src/linux_pci.c
@@ -1,7 +1,7 @@
/*-
* Copyright (c) 2015-2016 Mellanox Technologies, Ltd.
* All rights reserved.
- * Copyright (c) 2020-2025 The FreeBSD Foundation
+ * Copyright (c) 2020-2026 The FreeBSD Foundation
*
* Portions of this software were developed by Björn Zeeb
* under sponsorship from the FreeBSD Foundation.
@@ -28,6 +28,15 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+/*
+ * We have two ways to create a pci_dev (pdev):
+ * (1) coming from the device_attach DEVMETHOD, and
+ * (2) the other from manual creation via lkpinew_pci_dev().
+ *
+ * Only devices from (1) end up on our LinuxKPI global pci_devices list.
+ * All others are "place fillers" -- XXX if only "place filler" was always true.
+ */
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
@@ -100,6 +109,7 @@
static int linux_backlight_update_status(device_t dev, struct backlight_props *props);
static int linux_backlight_get_info(device_t dev, struct backlight_info *info);
static void lkpi_pcim_iomap_table_release(struct device *, void *);
+static void lkpinew_pci_dev_release(struct device *);
static device_method_t pci_methods[] = {
DEVMETHOD(device_probe, linux_pci_probe),
@@ -335,9 +345,82 @@
static void
lkpi_pci_dev_release(struct device *dev)
{
+ struct pci_dev *pdev;
+ /*
+ * The very first we have to free all the dynamic
+ * resource which are on the devres list.
+ * Otherwise we risk that supporting infrastructure
+ * is gone and we panic 'randomly'.
+ */
lkpi_devres_release_free_list(dev);
+
+ /*
+ * Now undo linux_pci_attach_device() in reverse-ish
+ * order.
+ */
+ pdev = to_pci_dev(dev);
+
+ /*
+ * pdrv->remove happens before pci_put_dev() in
+ * linux_pci_detach_device(), which means the driver should have
+ * cleaned up before we get here; see irqents and mmio below.
+ */
+
+ /* Clear the hierarchy recursively to root. */
+ if (pdev->bus->self != pdev) {
+ pci_dev_put(pdev->bus->self);
+ pdev->bus->self = NULL;
+ }
+
+ if (pdev->root != NULL) {
+ lkpinew_pci_dev_release(&pdev->root->dev); /* pci_dev_put(pdev->root); ? */
+ pdev->root = NULL;
+ }
+
+ spin_lock(&pci_lock);
+ list_del(&pdev->links);
+ spin_unlock(&pci_lock);
+
+ linux_pdev_dma_uninit(pdev);
+
+ /* irq? */
+
+ /* Undo lkpifill_pci_dev(). */
+ /* devres is gone already; went at the very top. */
+ if (!list_empty_careful(&pdev->dev.irqents)) {
+ dev_warn(&pdev->dev, "%s: driver did not clean up; "
+ "leaking IRQs\n", __func__);
+ /*
+ * XXX add private function to interrupt.h/linux_interrupt.c
+ * to walk the list and call free_irq on each if we have to.
+ */
+ }
+
spin_lock_destroy(&dev->devres_lock);
+ spin_lock_destroy(&pdev->pcie_cap_lock);
+
+ if (!TAILQ_EMPTY(&pdev->mmio)) {
+ dev_warn(&pdev->dev, "%s: driver did not clean up; "
+ "leaking mmio resources\n", __func__);
+ /* XXX we have two functions to walk and release in here. */
+ }
+
+ if (pdev->msi_desc != NULL) {
+ for (int i = pci_msi_count(pdev->dev.bsddev) - 1; i >= 0; i--)
+ free(pdev->msi_desc[i], M_DEVBUF);
+ free(pdev->msi_desc, M_DEVBUF);
+ }
+
+ free(pdev->bus, M_DEVBUF);
+ kfree(pdev->path_name);
+
+ /*
+ * Very last do an internal hack in order to signal
+ * that this was run (device reference fully dropped).
+ * See comment in linux_pci_detach_device().
+ */
+ pdev->dev.release = NULL;
}
static int
@@ -664,14 +747,16 @@
linux_pci_detach(device_t dev)
{
struct pci_dev *pdev;
+ int error;
pdev = device_get_softc(dev);
-
MPASS(pdev != NULL);
- device_set_desc(dev, NULL);
+ error = linux_pci_detach_device(pdev);
+ if (error == 0)
+ device_set_desc(dev, NULL);
- return (linux_pci_detach_device(pdev));
+ return (error);
}
int
@@ -680,21 +765,51 @@
linux_set_current(curthread);
+ /*
+ * We cannot do much here as almost everything will have
+ * to happen as the last reference to the LinuxKPI device
+ * goes away. That will call the release function,
+ * which lkpifill_pci_dev() set. That is were most
+ * of the cleanup will happen. But before that give
+ * the driver a chance to cleanup.
+ * The big problem is that the Linux KPI does not
+ * report back if it was the last kref (well kref
+ * does report back but then kobj, dev, pdev do not).
+ * So we have little way of knowing if the release
+ * happened or not. We have to play tricks for that
+ * and we can given the softc (pdev) is still valid
+ * until we return from here.
+ */
+
if (pdev->pdrv != NULL)
pdev->pdrv->remove(pdev);
- if (pdev->root != NULL)
- pci_dev_put(pdev->root);
- free(pdev->bus, M_DEVBUF);
- linux_pdev_dma_uninit(pdev);
+ pci_dev_put(pdev);
- spin_lock(&pci_lock);
- list_del(&pdev->links);
- spin_unlock(&pci_lock);
- spin_lock_destroy(&pdev->pcie_cap_lock);
- put_device(&pdev->dev);
+ /*
+ * We (ab)use the release function as a guard to
+ * know if we made it there and the device is gone.
+ */
+ if (pdev->dev.release != lkpi_pci_dev_release)
+ return (0);
- return (0);
+ /*
+ * Detach failed.
+ * We need to re-acquire the ref and wait for
+ * the other refs to be gone... In theory this
+ * should never happen, so log it!
+ * XXX I wish there was a KPI to query the ref.
+ *
+ * If we do not error and wait, we will have a
+ * LinuxKPI device dangling active with pointers
+ * but the FreeBSD device_t will be 'gone'.
+ */
+ device_printf(pdev->dev.bsddev, "%s failed due to %u other pending "
+ "references on the LinuxKPI device.\n", __func__,
+ kref_read(&pdev->dev.kobj.kref));
+ pci_dev_get(pdev);
+
+ return (EBUSY);
}
static int
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Jun 13, 11:58 PM (18 h, 31 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
33749348
Default Alt Text
D57429.id.diff (5 KB)
Attached To
Mode
D57429: LinuxKPI: pci detach: implement a proper detach (release) path
Attached
Detach File
Event Timeline
Log In to Comment