git: d3690a599586 - stable/15 - devfs: make destroy_dev() a release barrier for cdevpriv destructors runs
Date: Wed, 17 Dec 2025 21:07:52 UTC
The branch stable/15 has been updated by wulf:
URL: https://cgit.FreeBSD.org/src/commit/?id=d3690a5995867e35d4d1dc63127343d4b0a092d6
commit d3690a5995867e35d4d1dc63127343d4b0a092d6
Author: Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2025-10-23 18:37:00 +0000
Commit: Vladimir Kondratyev <wulf@FreeBSD.org>
CommitDate: 2025-12-17 21:05:38 +0000
devfs: make destroy_dev() a release barrier for cdevpriv destructors runs
Ensure that all destructors for cdevpriv finished running before
destroy_dev() returns to the caller. Otherwise, since
devfs_destroy_cdevpriv() removes the cdevpriv data from the list, drops
the cdevpriv_mtx, and then starts the destructor, it is possible for
destroy_dev() to return before destructor finished in other thread.
This should allow drivers to safely remove cdev instance data that might
be referenced by cdevpriv data.
Diagnosed by: kevans
Reviewed by: kevans, markj
Sponsored by: The FreeBSD Foundation
MFC after: 1 week
Differential revision: https://reviews.freebsd.org/D53303
(cherry picked from commit 4dbe6628179d8e6bf400bfdb4bfa869bdc102a56)
---
sys/fs/devfs/devfs_int.h | 1 +
sys/fs/devfs/devfs_vnops.c | 17 ++++++++++++++---
sys/kern/kern_conf.c | 3 +++
3 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/sys/fs/devfs/devfs_int.h b/sys/fs/devfs/devfs_int.h
index 916297425b53..9fa75c0e90ad 100644
--- a/sys/fs/devfs/devfs_int.h
+++ b/sys/fs/devfs/devfs_int.h
@@ -67,6 +67,7 @@ struct cdev_priv {
void *cdp_dtr_cb_arg;
LIST_HEAD(, cdev_privdata) cdp_fdpriv;
+ u_int cdp_fdpriv_dtrc;
struct mtx cdp_threadlock;
};
diff --git a/sys/fs/devfs/devfs_vnops.c b/sys/fs/devfs/devfs_vnops.c
index 3a64c205186f..880756264b0f 100644
--- a/sys/fs/devfs/devfs_vnops.c
+++ b/sys/fs/devfs/devfs_vnops.c
@@ -200,14 +200,25 @@ devfs_foreach_cdevpriv(struct cdev *dev, int (*cb)(void *data, void *arg),
void
devfs_destroy_cdevpriv(struct cdev_privdata *p)
{
+ struct file *fp;
+ struct cdev_priv *cdp;
mtx_assert(&cdevpriv_mtx, MA_OWNED);
- KASSERT(p->cdpd_fp->f_cdevpriv == p,
- ("devfs_destoy_cdevpriv %p != %p", p->cdpd_fp->f_cdevpriv, p));
- p->cdpd_fp->f_cdevpriv = NULL;
+ fp = p->cdpd_fp;
+ KASSERT(fp->f_cdevpriv == p,
+ ("devfs_destoy_cdevpriv %p != %p", fp->f_cdevpriv, p));
+ cdp = cdev2priv((struct cdev *)fp->f_data);
+ cdp->cdp_fdpriv_dtrc++;
+ fp->f_cdevpriv = NULL;
LIST_REMOVE(p, cdpd_list);
mtx_unlock(&cdevpriv_mtx);
(p->cdpd_dtr)(p->cdpd_data);
+ mtx_lock(&cdevpriv_mtx);
+ MPASS(cdp->cdp_fdpriv_dtrc >= 1);
+ cdp->cdp_fdpriv_dtrc--;
+ if (cdp->cdp_fdpriv_dtrc == 0)
+ wakeup(&cdp->cdp_fdpriv_dtrc);
+ mtx_unlock(&cdevpriv_mtx);
free(p, M_CDEVPDATA);
}
diff --git a/sys/kern/kern_conf.c b/sys/kern/kern_conf.c
index 57411b0dec08..2da51d84ff60 100644
--- a/sys/kern/kern_conf.c
+++ b/sys/kern/kern_conf.c
@@ -1163,6 +1163,9 @@ destroy_devl(struct cdev *dev)
devfs_destroy_cdevpriv(p);
mtx_lock(&cdevpriv_mtx);
}
+ while (cdp->cdp_fdpriv_dtrc != 0) {
+ msleep(&cdp->cdp_fdpriv_dtrc, &cdevpriv_mtx, 0, "cdfdpc", 0);
+ }
mtx_unlock(&cdevpriv_mtx);
dev_lock();