Module Name: src Committed By: riastradh Date: Mon Mar 28 12:37:27 UTC 2022
Modified Files: src/sys/miscfs/specfs: spec_vnops.c Log Message: specfs: Take an I/O reference across bdev/cdev_open. - Revoke is used to invalidate all prior access control checks when device permissions are changing, so it must wait for .d_open to exit so any new access must go through new access control checks. - Revoke is used by vdevgone in xyz_detach to wait until all use of the driver's data structures have completed before xyz_detach frees them. So we need to make sure spec_close waits for .d_open too. To generate a diff of this commit: cvs rdiff -u -r1.205 -r1.206 src/sys/miscfs/specfs/spec_vnops.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/miscfs/specfs/spec_vnops.c diff -u src/sys/miscfs/specfs/spec_vnops.c:1.205 src/sys/miscfs/specfs/spec_vnops.c:1.206 --- src/sys/miscfs/specfs/spec_vnops.c:1.205 Mon Mar 28 12:37:18 2022 +++ src/sys/miscfs/specfs/spec_vnops.c Mon Mar 28 12:37:26 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: spec_vnops.c,v 1.205 2022/03/28 12:37:18 riastradh Exp $ */ +/* $NetBSD: spec_vnops.c,v 1.206 2022/03/28 12:37:26 riastradh Exp $ */ /*- * Copyright (c) 2008 The NetBSD Foundation, Inc. @@ -58,7 +58,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: spec_vnops.c,v 1.205 2022/03/28 12:37:18 riastradh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: spec_vnops.c,v 1.206 2022/03/28 12:37:26 riastradh Exp $"); #include <sys/param.h> #include <sys/proc.h> @@ -694,10 +694,10 @@ spec_open(void *v) } */ *ap = v; struct lwp *l = curlwp; struct vnode *vp = ap->a_vp; - dev_t dev; + dev_t dev, dev1; int error; enum kauth_device_req req; - specnode_t *sn; + specnode_t *sn, *sn1; specdev_t *sd; spec_ioctl_t ioctl; u_int gen = 0; @@ -805,18 +805,34 @@ spec_open(void *v) } /* - * Open the device. If .d_open returns ENXIO (device not - * configured), the driver may not be loaded, so try - * autoloading a module and then try .d_open again if anything - * got loaded. - * * Because opening the device may block indefinitely, e.g. when * opening a tty, and loading a module may cross into many * other subsystems, we must not hold the vnode lock while * calling .d_open, so release it now and reacquire it when * done. + * + * Take an I/O reference so that any concurrent spec_close via + * spec_node_revoke will wait for us to finish calling .d_open. + * The vnode can't be dead at this point because we have it + * locked. Note that if revoked, the driver must interrupt + * .d_open before spec_close starts waiting for I/O to drain so + * this doesn't deadlock. */ VOP_UNLOCK(vp); + error = spec_io_enter(vp, &sn1, &dev1); + if (error) { + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + return error; + } + KASSERT(sn1 == sn); + KASSERT(dev1 == dev); + + /* + * Open the device. If .d_open returns ENXIO (device not + * configured), the driver may not be loaded, so try + * autoloading a module and then try .d_open again if anything + * got loaded. + */ switch (vp->v_type) { case VCHR: do { @@ -871,7 +887,16 @@ spec_open(void *v) default: __unreachable(); } + + /* + * Release the I/O reference now that we have called .d_open, + * and reacquire the vnode lock. At this point, the device may + * have been revoked, so we must tread carefully. However, sn + * and sd remain valid pointers until we drop our reference. + */ + spec_io_exit(vp, sn); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + KASSERT(vp->v_specnode == sn); /* * If it has been revoked since we released the vnode lock and