From f83552ead5ed3f364ab6711ad1d3663615b43cef Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh@NetBSD.org>
Date: Tue, 4 Feb 2020 03:50:40 +0000
Subject: [PATCH] Use specfs vnops for specnodes in kernfs.

While here, don't filter out rootdev and rrootdev merely because
they're not cached.

Fixes the elusive /kern/rootdev and /kern/rrootdev nodes, which only
appeared sometimes when they felt like it, and, e.g., fixes all
operations on /kern/rootdev and /kern/rrootdev returning EOPNOTSUPP.

We didn't seem to have a single PR for these issues but the following
PRs are all relevant:

PR bin/13564
PR kern/38265
PR kern/38778
PR kern/45974

XXX pullup-9, pullup-8, pullup-7, pullup-6, pullup-5, pullup-4, pullup-3, pullup-2, pullup-1.4T...
---
 sys/miscfs/kernfs/kernfs.h        |  1 +
 sys/miscfs/kernfs/kernfs_vfsops.c |  3 ++
 sys/miscfs/kernfs/kernfs_vnops.c  | 66 ++++++++++++++++++++++---------
 3 files changed, 52 insertions(+), 18 deletions(-)

diff --git a/sys/miscfs/kernfs/kernfs.h b/sys/miscfs/kernfs/kernfs.h
index e4e79143f885..de6de4282008 100644
--- a/sys/miscfs/kernfs/kernfs.h
+++ b/sys/miscfs/kernfs/kernfs.h
@@ -122,6 +122,7 @@ extern const struct kern_target kern_targets[];
 extern int nkern_targets;
 extern const int static_nkern_targets;
 extern int (**kernfs_vnodeop_p)(void *);
+extern int (**kernfs_specop_p)(void *);
 extern struct vfsops kernfs_vfsops;
 extern dev_t rrootdev;
 extern kmutex_t kfs_lock;
diff --git a/sys/miscfs/kernfs/kernfs_vfsops.c b/sys/miscfs/kernfs/kernfs_vfsops.c
index b6c43d938849..7622168e7440 100644
--- a/sys/miscfs/kernfs/kernfs_vfsops.c
+++ b/sys/miscfs/kernfs/kernfs_vfsops.c
@@ -283,6 +283,7 @@ again:
 		vp->v_vflag = VV_ROOT;
 
 	if (kt->kt_tag == KFSdevice) {
+		vp->v_op = kernfs_specop_p;
 		spec_node_init(vp, *(dev_t *)kt->kt_data);
 	}
 
@@ -293,9 +294,11 @@ again:
 }
 
 extern const struct vnodeopv_desc kernfs_vnodeop_opv_desc;
+extern const struct vnodeopv_desc kernfs_specop_opv_desc;
 
 const struct vnodeopv_desc * const kernfs_vnodeopv_descs[] = {
 	&kernfs_vnodeop_opv_desc,
+	&kernfs_specop_opv_desc,
 	NULL,
 };
 
diff --git a/sys/miscfs/kernfs/kernfs_vnops.c b/sys/miscfs/kernfs/kernfs_vnops.c
index 3acb66218d85..824429f909be 100644
--- a/sys/miscfs/kernfs/kernfs_vnops.c
+++ b/sys/miscfs/kernfs/kernfs_vnops.c
@@ -227,6 +227,54 @@ const struct vnodeopv_entry_desc kernfs_vnodeop_entries[] = {
 const struct vnodeopv_desc kernfs_vnodeop_opv_desc =
 	{ &kernfs_vnodeop_p, kernfs_vnodeop_entries };
 
+int (**kernfs_specop_p)(void *);
+const struct vnodeopv_entry_desc kernfs_specop_entries[] = {
+	{ &vop_default_desc, vn_default_error },
+	{ &vop_lookup_desc, spec_lookup },		/* lookup */
+	{ &vop_create_desc, spec_create },		/* create */
+	{ &vop_mknod_desc, spec_mknod },		/* mknod */
+	{ &vop_open_desc, spec_open },			/* open */
+	{ &vop_close_desc, spec_close },		/* close */
+	{ &vop_access_desc, kernfs_access },		/* access */
+	{ &vop_getattr_desc, kernfs_getattr },		/* getattr */
+	{ &vop_setattr_desc, kernfs_setattr },		/* setattr */
+	{ &vop_read_desc, spec_read },			/* read */
+	{ &vop_write_desc, spec_write },		/* write */
+	{ &vop_fallocate_desc, spec_fallocate },	/* fallocate */
+	{ &vop_fdiscard_desc, spec_fdiscard },		/* fdiscard */
+	{ &vop_fcntl_desc, spec_fcntl },		/* fcntl */
+	{ &vop_ioctl_desc, spec_ioctl },		/* ioctl */
+	{ &vop_poll_desc, spec_poll },			/* poll */
+	{ &vop_revoke_desc, spec_revoke },		/* revoke */
+	{ &vop_fsync_desc, spec_fsync },		/* fsync */
+	{ &vop_seek_desc, spec_seek },			/* seek */
+	{ &vop_remove_desc, spec_remove },		/* remove */
+	{ &vop_link_desc, spec_link },			/* link */
+	{ &vop_rename_desc, spec_rename },		/* rename */
+	{ &vop_mkdir_desc, spec_mkdir },		/* mkdir */
+	{ &vop_rmdir_desc, spec_rmdir },		/* rmdir */
+	{ &vop_symlink_desc, spec_symlink },		/* symlink */
+	{ &vop_readdir_desc, spec_readdir },		/* readdir */
+	{ &vop_readlink_desc, spec_readlink },		/* readlink */
+	{ &vop_abortop_desc, spec_abortop },		/* abortop */
+	{ &vop_inactive_desc, kernfs_inactive },	/* inactive */
+	{ &vop_reclaim_desc, kernfs_reclaim },		/* reclaim */
+	{ &vop_lock_desc, kernfs_lock },		/* lock */
+	{ &vop_unlock_desc, kernfs_unlock },		/* unlock */
+	{ &vop_bmap_desc, spec_bmap },			/* bmap */
+	{ &vop_strategy_desc, spec_strategy },		/* strategy */
+	{ &vop_print_desc, kernfs_print },		/* print */
+	{ &vop_islocked_desc, kernfs_islocked },	/* islocked */
+	{ &vop_pathconf_desc, spec_pathconf },		/* pathconf */
+	{ &vop_advlock_desc, spec_advlock },		/* advlock */
+	{ &vop_bwrite_desc, spec_bwrite },		/* bwrite */
+	{ &vop_getpages_desc, spec_getpages },		/* getpages */
+	{ &vop_putpages_desc, spec_putpages },		/* putpages */
+	{ NULL, NULL }
+};
+const struct vnodeopv_desc kernfs_specop_opv_desc =
+	{ &kernfs_specop_p, kernfs_specop_entries };
+
 static inline int
 kernfs_fileop_compare(struct kernfs_fileop *a, struct kernfs_fileop *b)
 {
@@ -946,15 +994,6 @@ kernfs_readdir(void *v)
 					break;
 				kt = &dkt->dkt_kt;
 			}
-			if (kt->kt_tag == KFSdevice) {
-				dev_t *dp = kt->kt_data;
-				struct vnode *fvp;
-
-				if (*dp == NODEV ||
-				    !vfinddev(*dp, kt->kt_vtype, &fvp))
-					continue;
-				vrele(fvp);
-			}
 			if (kt->kt_tag == KFSmsgbuf) {
 				if (!logenabled(msgbufp)) {
 					continue;
@@ -1028,15 +1067,6 @@ kernfs_readdir(void *v)
 				kt = &dkt->dkt_kt;
 				dkt = SIMPLEQ_NEXT(dkt, dkt_queue);
 			}
-			if (kt->kt_tag == KFSdevice) {
-				dev_t *dp = kt->kt_data;
-				struct vnode *fvp;
-
-				if (*dp == NODEV ||
-				    !vfinddev(*dp, kt->kt_vtype, &fvp))
-					continue;
-				vrele(fvp);
-			}
 			d.d_namlen = kt->kt_namlen;
 			if ((error = kernfs_setdirentfileno(&d, i, kfs,
 			    ks->ks_parent, kt, ap)) != 0)