From c2a6ddbd60154ee7769e298d561c5a359c30e18e Mon Sep 17 00:00:00 2001 From: Taylor R Campbell Date: Fri, 4 Feb 2022 23:27:17 +0000 Subject: [PATCH 12/13] squash! specfs: Drain all I/O operations after last .d_close call. - Avoid sd_iocnt overflow in spec_io_enter -- unlikely but possible if pud(4) is involved. - Assert no underflow in spec_io_exit. --- sys/miscfs/specfs/spec_vnops.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/sys/miscfs/specfs/spec_vnops.c b/sys/miscfs/specfs/spec_vnops.c index a41ecbac89ff..8e2b7d042ecb 100644 --- a/sys/miscfs/specfs/spec_vnops.c +++ b/sys/miscfs/specfs/spec_vnops.c @@ -271,9 +271,25 @@ spec_io_enter(struct vnode *vp, struct specnode **snp, dev_t *devp) * already have to take the interlock for vdead_check, so * there's not much more cost here to another atomic operation. */ - iocnt = atomic_inc_uint_nv(&sn->sn_dev->sd_iocnt); - CTASSERT(MAXLWP < UINT_MAX); - KASSERT(iocnt < UINT_MAX); + do { + iocnt = atomic_load_relaxed(&sn->sn_dev->sd_iocnt); + if (__predict_false(iocnt == UINT_MAX)) { + /* + * The I/O count is limited by the number of + * LWPs (which will never overflow this) -- + * unless one driver uses another driver via + * specfs, which is rather unusual, but which + * could happen via pud(4) userspace drivers. + * We could use a 64-bit count, but can't use + * atomics for that on all platforms. + * (Probably better to switch to psref or + * localcount instead.) + */ + error = EBUSY; + goto out; + } + } while (atomic_cas_uint(&sn->sn_dev->sd_iocnt, iocnt, iocnt + 1) + != iocnt); /* Success! */ *snp = sn; @@ -307,6 +323,7 @@ spec_io_exit(struct vnode *vp, struct specnode *sn) */ do { iocnt = atomic_load_relaxed(&sd->sd_iocnt); + KASSERT(iocnt > 0); if (iocnt == 1) { mutex_enter(&device_lock); if (atomic_dec_uint_nv(&sd->sd_iocnt) == 0)