From owner-freebsd-fs@freebsd.org Sun Feb 25 06:42:02 2018 Return-Path: Delivered-To: freebsd-fs@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 3669DF1C201 for ; Sun, 25 Feb 2018 06:42:02 +0000 (UTC) (envelope-from mckusick@mckusick.com) Received: from chez.mckusick.com (chez.mckusick.com [70.36.157.235]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id B6CA86ED29 for ; Sun, 25 Feb 2018 06:42:01 +0000 (UTC) (envelope-from mckusick@mckusick.com) Received: from chez.mckusick.com (localhost [IPv6:::1]) by chez.mckusick.com (8.15.2/8.15.2) with ESMTP id w1P6g8Wm064509; Sat, 24 Feb 2018 22:42:08 -0800 (PST) (envelope-from mckusick@mckusick.com) Message-Id: <201802250642.w1P6g8Wm064509@chez.mckusick.com> From: Kirk McKusick To: Aijaz Baig Subject: Re: Regarding vop_vector cc: freebsd-fs@freebsd.org X-URL: http://WWW.McKusick.COM/ Reply-To: Kirk McKusick In-reply-to: Comments: In-reply-to Aijaz Baig message dated "Sun, 25 Feb 2018 07:24:12 +0530." MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-ID: <64507.1519540928.1@chez.mckusick.com> Content-Transfer-Encoding: quoted-printable Date: Sat, 24 Feb 2018 22:42:08 -0800 X-Spam-Status: No, score=-1.4 required=5.0 tests=BAYES_00,MISSING_MID, UNPARSEABLE_RELAY autolearn=no autolearn_force=no version=3.4.1 X-Spam-Checker-Version: SpamAssassin 3.4.1 (2015-04-28) on chez.mckusick.com X-BeenThere: freebsd-fs@freebsd.org X-Mailman-Version: 2.1.25 Precedence: list List-Id: Filesystems List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 25 Feb 2018 06:42:02 -0000 > From: Aijaz Baig > Date: Sun, 25 Feb 2018 07:24:12 +0530 > Subject: Regarding vop_vector > To: freebsd-fs@freebsd.org > = > Hello > = > I am trying to understand how the VFS layer within FreeBSD and I was rat= her > stumped while trying to find where vop_vector was declared. Upon searchi= ng > the internet, realized that an awk script is used to "generate" this lik= e so: > = > /sys/tools/vnode_if.awk /sys/kern/vnode_if.src -q > = > So I was wondering if anyone could provide me a (brief would be fine as > well) walk through the memory lane as to why such an 'odd looking' way w= as > adopted (perhaps it is brilliant but my thick skull is unable to fathom > it's brilliance). > = > Keen to hear from you experts out there please! > -- = > Best Regards, > Aijaz Baig As the person that came up with this idea (in the 1980's) let me try to explain my thinking. Suppose you want to add a new VFS operation. Before I created the script, you had to create macros for it in at least four different files. Now you just need to add a concise and readable descripti= on of it in /sys/kern/vnode_if.src then run the script and all the boilerplat= e gets generated for you. Let me give you a short example. Consider the VFS operator to check to see if a vnode is locked, typically used as if (VOP_ISLOCKED(vp)) { do something with locked vp; } The description of it in /sys/kern/vnode_if.src is three lines: vop_islocked { IN struct vnode *vp; }; Here is what is generated for it in the compile directory by the awk scrip= t. In /sys/amd64/compile/GENERIC/vnode_if.c: static int vop_islocked_vp_offsets[] =3D { VOPARG_OFFSETOF(struct vop_islocked_args,a_vp), VDESC_NO_OFFSET }; SDT_PROBE_DEFINE2(vfs, vop, vop_islocked, entry, "struct vnode *", "struct= vop_islocked_args *"); SDT_PROBE_DEFINE3(vfs, vop, vop_islocked, return, "struct vnode *", "struc= t vop_islocked_args *", "int"); int VOP_ISLOCKED_AP(struct vop_islocked_args *a) { return(VOP_ISLOCKED_APV(a->a_vp->v_op, a)); } int VOP_ISLOCKED_APV(struct vop_vector *vop, struct vop_islocked_args *a) { int rc; VNASSERT(a->a_gen.a_desc =3D=3D &vop_islocked_desc, a->a_vp, ("Wrong a_desc in vop_islocked(%p, %p)", a->a_vp, a)); while(vop !=3D NULL && \ vop->vop_islocked =3D=3D NULL && vop->vop_bypass =3D=3D NULL) vop =3D vop->vop_default; VNASSERT(vop !=3D NULL, a->a_vp, ("No vop_islocked(%p, %p)", a->a_vp, a))= ; SDT_PROBE2(vfs, vop, vop_islocked, entry, a->a_vp, a); KTR_START1(KTR_VOP, "VOP", "VOP_ISLOCKED", (uintptr_t)a, "vp:0x%jX", (uintptr_t)a->a_vp); VFS_PROLOGUE(a->a_vp->v_mount); if (vop->vop_islocked !=3D NULL) rc =3D vop->vop_islocked(a); else rc =3D vop->vop_bypass(&a->a_gen); VFS_EPILOGUE(a->a_vp->v_mount); SDT_PROBE3(vfs, vop, vop_islocked, return, a->a_vp, a, rc); if (rc =3D=3D 0) { } else { } KTR_STOP1(KTR_VOP, "VOP", "VOP_ISLOCKED", (uintptr_t)a, "vp:0x%jX", (uintptr_t)a->a_vp); return (rc); } struct vnodeop_desc vop_islocked_desc =3D { "vop_islocked", 0, (vop_bypass_t *)VOP_ISLOCKED_AP, vop_islocked_vp_offsets, VDESC_NO_OFFSET, VDESC_NO_OFFSET, VDESC_NO_OFFSET, VDESC_NO_OFFSET, }; In /sys/amd64/compile/GENERIC/vnode_if.h: struct vop_islocked_args { struct vop_generic_args a_gen; struct vnode *a_vp; }; extern struct vnodeop_desc vop_islocked_desc; int VOP_ISLOCKED_AP(struct vop_islocked_args *); int VOP_ISLOCKED_APV(struct vop_vector *vop, struct vop_islocked_args *); static __inline int VOP_ISLOCKED( struct vnode *vp) { struct vop_islocked_args a; a.a_gen.a_desc =3D &vop_islocked_desc; a.a_vp =3D vp; return (VOP_ISLOCKED_APV(vp->v_op, &a)); } In /sys/amd64/compile/GENERIC/vnode_if_newproto.h, an entry in the vop_vector array: struct vop_vector { struct vop_vector *vop_default; vop_bypass_t *vop_bypass; vop_islocked_t *vop_islocked; ... }; And finally in /sys/amd64/compile/GENERIC/vnode_if_typedef.h: struct vop_islocked_args; typedef int vop_islocked_t(struct vop_islocked_args *); And absent this script, every time you wanted to make a change in the boilerplate, you would have to go through and fix it for every existing VFS operator (and trust me the boilerplate has changed a *lot* since I first did it in the 1980's :-) Kirk McKusick