Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/sys/miscfs/specfs specfs: Split spec_open switch into three ...
details: https://anonhg.NetBSD.org/src/rev/ed8897af141e
branches: trunk
changeset: 364500:ed8897af141e
user: riastradh <riastradh%NetBSD.org@localhost>
date: Mon Mar 28 12:34:59 2022 +0000
description:
specfs: Split spec_open switch into three sections.
The sections are now:
1. Acquire open reference.
1a (intermezzo). Set VV_ISTTY.
2. Drop the vnode lock to call .d_open and autoload modules if
necessary.
3. Handle concurrent revoke if it happenend, or release open reference
if .d_open failed.
No functional change. Sprinkle comments about problems.
diffstat:
sys/miscfs/specfs/spec_vnops.c | 131 +++++++++++++++++++++++++++++++++-------
1 files changed, 107 insertions(+), 24 deletions(-)
diffs (177 lines):
diff -r b07d199beb02 -r ed8897af141e sys/miscfs/specfs/spec_vnops.c
--- a/sys/miscfs/specfs/spec_vnops.c Mon Mar 28 12:34:51 2022 +0000
+++ b/sys/miscfs/specfs/spec_vnops.c Mon Mar 28 12:34:59 2022 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: spec_vnops.c,v 1.189 2022/03/28 12:34:51 riastradh Exp $ */
+/* $NetBSD: spec_vnops.c,v 1.190 2022/03/28 12:34:59 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.189 2022/03/28 12:34:51 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: spec_vnops.c,v 1.190 2022/03/28 12:34:59 riastradh Exp $");
#include <sys/param.h>
#include <sys/proc.h>
@@ -550,6 +550,20 @@
if (error != 0)
return (error);
+ /*
+ * Acquire an open reference -- as long as we hold onto it, and
+ * the vnode isn't revoked, it can't be closed.
+ *
+ * But first check whether it has been revoked -- if so, we
+ * can't acquire more open references and we must fail
+ * immediately with EBADF.
+ *
+ * XXX This races with revoke: once we release the vnode lock,
+ * the vnode may be revoked, and the .d_close callback run, at
+ * the same time as we're calling .d_open here. Drivers
+ * shouldn't have to contemplate this scenario; .d_open and
+ * .d_close should be prevented from running concurrently.
+ */
switch (vp->v_type) {
case VCHR:
/*
@@ -564,8 +578,68 @@
sd->sd_opencnt++;
sn->sn_opencnt++;
mutex_exit(&device_lock);
+ break;
+ case VBLK:
+ /*
+ * For block devices, permit only one open. The buffer
+ * cache cannot remain self-consistent with multiple
+ * vnodes holding a block device open.
+ *
+ * Treat zero opencnt with non-NULL mountpoint as open.
+ * This may happen after forced detach of a mounted device.
+ */
+ mutex_enter(&device_lock);
+ if (sn->sn_gone) {
+ mutex_exit(&device_lock);
+ return (EBADF);
+ }
+ if (sd->sd_opencnt != 0 || sd->sd_mountpoint != NULL) {
+ mutex_exit(&device_lock);
+ return EBUSY;
+ }
+ sn->sn_opencnt = 1;
+ sd->sd_opencnt = 1;
+ sd->sd_bdevvp = vp;
+ mutex_exit(&device_lock);
+ break;
+ default:
+ panic("invalid specfs vnode type: %d", vp->v_type);
+ }
+
+ /*
+ * Set VV_ISTTY if this is a tty cdev.
+ *
+ * XXX This does the wrong thing if the module has to be
+ * autoloaded. We should maybe set this after autoloading
+ * modules and calling .d_open successfully, except (a) we need
+ * the vnode lock to touch it, and (b) once we acquire the
+ * vnode lock again, the vnode may have been revoked, and
+ * deadfs's dead_read needs VV_ISTTY to be already set in order
+ * to return the right answer. So this needs some additional
+ * synchronization to be made to work correctly with tty driver
+ * module autoload. For now, let's just hope it doesn't cause
+ * too much trouble for a tty from an autoloaded driver module
+ * to fail with EIO instead of returning EOF.
+ */
+ if (vp->v_type == VCHR) {
if (cdev_type(dev) == D_TTY)
vp->v_vflag |= VV_ISTTY;
+ }
+
+ /*
+ * 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.
+ */
+ switch (vp->v_type) {
+ case VCHR:
VOP_UNLOCK(vp);
do {
const struct cdevsw *cdev;
@@ -594,27 +668,6 @@
break;
case VBLK:
- /*
- * For block devices, permit only one open. The buffer
- * cache cannot remain self-consistent with multiple
- * vnodes holding a block device open.
- *
- * Treat zero opencnt with non-NULL mountpoint as open.
- * This may happen after forced detach of a mounted device.
- */
- mutex_enter(&device_lock);
- if (sn->sn_gone) {
- mutex_exit(&device_lock);
- return (EBADF);
- }
- if (sd->sd_opencnt != 0 || sd->sd_mountpoint != NULL) {
- mutex_exit(&device_lock);
- return EBUSY;
- }
- sn->sn_opencnt = 1;
- sd->sd_opencnt = 1;
- sd->sd_bdevvp = vp;
- mutex_exit(&device_lock);
VOP_UNLOCK(vp);
do {
const struct bdevsw *bdev;
@@ -643,9 +696,39 @@
break;
default:
- panic("invalid specfs vnode type: %d", vp->v_type);
+ __unreachable();
}
+ /*
+ * If it has been revoked since we released the vnode lock and
+ * reacquired it, then spec_node_revoke has closed it, and we
+ * must fail with EBADF.
+ *
+ * Otherwise, if opening it failed, back out and release the
+ * open reference.
+ *
+ * XXX This is wrong -- we might release the last open
+ * reference here, but we don't close the device. If only this
+ * thread's call to open failed, that's fine, but we might
+ * have:
+ *
+ * Thread 1 Thread 2
+ * VOP_OPEN
+ * ...
+ * .d_open -> 0 (success)
+ * acquire vnode lock
+ * do stuff VOP_OPEN
+ * release vnode lock ...
+ * .d_open -> EBUSY
+ * VOP_CLOSE
+ * acquire vnode lock
+ * --sd_opencnt != 0
+ * => no .d_close
+ * release vnode lock
+ * acquire vnode lock
+ * --sd_opencnt == 0
+ * but no .d_close (***)
+ */
mutex_enter(&device_lock);
if (sn->sn_gone) {
if (error == 0)
Home |
Main Index |
Thread Index |
Old Index