NetBSD/sys/kern/vnode_if.sh
thorpej 11afd11faa Add a new VNODE_LOCKDEBUG option, which enables checks in the VOP_*()
calls to ensure that the vnode lock state is as expected when the VOP
call is made.  Modify vnode_if.src to set the expected state according
to the documenting lock table for each VOP.  Modify vnode_if.sh to emit
the checks.

Notes:
- The checks are only performed if the vnode has the VLOCKSWORK bit
  set.  Some file systems (e.g. specfs) don't even bother with vnode
  locks, so of course the checks will fail.
- We can't actually run with VNODE_LOCKDEBUG because there are so many
  vnode locking problems, not the least of which is the "use SHARED for
  VOP_READ()" issue, which screws things up for the entire call chain.

Inspired by similar changes in OpenBSD, but implemented differently.
2004-09-21 03:10:35 +00:00

477 lines
12 KiB
Bash

#!/bin/sh -
copyright="\
/*
* Copyright (c) 1992, 1993, 1994, 1995
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS \`\`AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
"
SCRIPT_ID='$NetBSD: vnode_if.sh,v 1.35 2004/09/21 03:10:35 thorpej Exp $'
# Script to produce VFS front-end sugar.
#
# usage: vnode_if.sh srcfile
# (where srcfile is currently /sys/kern/vnode_if.src)
#
if [ $# -ne 1 ] ; then
echo 'usage: vnode_if.sh srcfile'
exit 1
fi
# Name and revision of the source file.
src=$1
SRC_ID=`head -1 $src | sed -e 's/.*\$\(.*\)\$.*/\1/'`
# Names of the created files.
out_c=vnode_if.c
out_h=../sys/vnode_if.h
# Awk program (must support nawk extensions)
# Use "awk" at Berkeley, "nawk" or "gawk" elsewhere.
awk=${AWK:-awk}
# Does this awk have a "toupper" function? (i.e. is it GNU awk)
isgawk=`$awk 'BEGIN { print toupper("true"); exit; }' 2>/dev/null`
# If this awk does not define "toupper" then define our own.
if [ "$isgawk" = TRUE ] ; then
# GNU awk provides it.
toupper=
else
# Provide our own toupper()
toupper='
function toupper(str) {
_toupper_cmd = "echo "str" |tr a-z A-Z"
_toupper_cmd | getline _toupper_str;
close(_toupper_cmd);
return _toupper_str;
}'
fi
#
# This is the common part of all awk programs that read $src
# This parses the input for one function into the arrays:
# argdir, argtype, argname, willrele
# and calls "doit()" to generate output for the function.
#
# Input to this parser is pre-processed slightly by sed
# so this awk parser doesn't have to work so hard. The
# changes done by the sed pre-processing step are:
# insert a space beween * and pointer name
# replace semicolons with spaces
#
sed_prep='s:\*\([^\*/]\):\* \1:g
s/;/ /'
awk_parser='
# Comment line
/^#/ { next; }
# First line of description
/^vop_/ {
name=$1;
argc=0;
next;
}
# Last line of description
/^}/ {
doit();
next;
}
# Middle lines of description
{
argdir[argc] = $1; i=2;
if ($2 == "LOCKED=YES") {
lockstate[argc] = 1;
i++;
} else if ($2 == "LOCKED=NO") {
lockstate[argc] = 0;
i++;
} else
lockstate[argc] = -1;
if ($2 == "WILLRELE" ||
$3 == "WILLRELE") {
willrele[argc] = 1;
i++;
} else if ($2 == "WILLUNLOCK" ||
$3 == "WILLUNLOCK") {
willrele[argc] = 2;
i++;
} else if ($2 == "WILLPUT" ||
$3 == "WILLPUT") {
willrele[argc] = 3;
i++;
} else
willrele[argc] = 0;
argtype[argc] = $i; i++;
while (i < NF) {
argtype[argc] = argtype[argc]" "$i;
i++;
}
argname[argc] = $i;
argc++;
next;
}
'
# This is put before the copyright on each generated file.
warning="\
/* @NetBSD@ */
/*
* Warning: This file is generated automatically.
* (Modifications made here may easily be lost!)
*
* Created from the file:
* ${SRC_ID}
* by the script:
* ${SCRIPT_ID}
*/
"
# This is to satisfy McKusick (get rid of evil spaces 8^)
anal_retentive='s:\([^/]\*\) :\1:g'
#
# Redirect stdout to the H file.
#
echo "$0: Creating $out_h" 1>&2
exec > $out_h
# Begin stuff
echo -n "$warning" | sed -e 's/\$//g;s/@/\$/g'
echo ""
echo -n "$copyright"
echo ''
echo '#ifndef _SYS_VNODE_IF_H_'
echo '#define _SYS_VNODE_IF_H_'
echo ''
echo '#ifdef _KERNEL'
echo '#if defined(_LKM) || defined(LKM)'
echo '/* LKMs always use non-inlined vnode ops. */'
echo '#define VNODE_OP_NOINLINE'
echo '#else'
echo '#include "opt_vnode_op_noinline.h"'
echo '#endif /* _LKM || LKM */'
echo '#ifdef _KERNEL_OPT'
echo '#include "opt_vnode_lockdebug.h"'
echo '#endif /* _KERNEL_OPT */'
echo '#endif /* _KERNEL */'
echo '
extern const struct vnodeop_desc vop_default_desc;
'
# Body stuff
# This awk program needs toupper() so define it if necessary.
sed -e "$sed_prep" $src | $awk "$toupper"'
function doit() {
# Declare arg struct, descriptor.
printf("\nstruct %s_args {\n", name);
printf("\tconst struct vnodeop_desc * a_desc;\n");
for (i=0; i<argc; i++) {
printf("\t%s a_%s;\n", argtype[i], argname[i]);
}
printf("};\n");
printf("extern const struct vnodeop_desc %s_desc;\n", name);
# Prototype it.
printf("#ifndef VNODE_OP_NOINLINE\n");
printf("static __inline\n");
printf("#endif\n");
protoarg = sprintf("int %s(", toupper(name));
protolen = length(protoarg);
printf("%s", protoarg);
for (i=0; i<argc; i++) {
protoarg = sprintf("%s", argtype[i]);
if (i < (argc-1)) protoarg = (protoarg ", ");
arglen = length(protoarg);
if ((protolen + arglen) > 77) {
protoarg = ("\n " protoarg);
arglen += 4;
protolen = 0;
}
printf("%s", protoarg);
protolen += arglen;
}
printf(")\n");
printf("#ifndef VNODE_OP_NOINLINE\n");
printf("__attribute__((__unused__))\n");
printf("#endif\n");
printf(";\n");
# Define inline function.
printf("#ifndef VNODE_OP_NOINLINE\n");
printf("static __inline int %s(", toupper(name));
for (i=0; i<argc; i++) {
printf("%s", argname[i]);
if (i < (argc-1)) printf(", ");
}
printf(")\n");
for (i=0; i<argc; i++) {
printf("\t%s %s;\n", argtype[i], argname[i]);
}
printf("{\n\tstruct %s_args a;\n", name);
printf("#ifdef VNODE_LOCKDEBUG\n");
for (i=0; i<argc; i++) {
if (lockstate[i] != -1)
printf("\tint islocked_%s;\n", argname[i]);
}
printf("#endif\n");
printf("\ta.a_desc = VDESC(%s);\n", name);
for (i=0; i<argc; i++) {
printf("\ta.a_%s = %s;\n", argname[i], argname[i]);
if (lockstate[i] != -1) {
printf("#ifdef VNODE_LOCKDEBUG\n");
printf("\tislocked_%s = (%s->v_flag & VLOCKSWORK) ? (VOP_ISLOCKED(%s) == LK_EXCLUSIVE) : %d;\n",
argname[i], argname[i], argname[i], lockstate[i]);
printf("\tif (islocked_%s != %d)\n", argname[i],
lockstate[i]);
printf("\t\tpanic(\"%s: %s: locked %%d, expected %%d\", islocked_%s, %d);\n", name, argname[i], argname[i], lockstate[i]);
printf("#endif\n");
}
}
printf("\treturn (VCALL(%s%s, VOFFSET(%s), &a));\n}\n",
argname[0], arg0special, name);
printf("#endif\n");
vops++;
}
BEGIN {
arg0special="";
vops = 1; # start at 1, to count the 'default' op
}
END {
printf("\n/* Special cases: */\n#include <sys/buf.h>\n");
argc=1;
argtype[0]="struct buf *";
argname[0]="bp";
lockstate[0] = -1;
arg0special="->b_vp";
name="vop_bwrite";
doit();
printf("\n#define VNODE_OPS_COUNT\t%d\n", vops);
}
'"$awk_parser" | sed -e "$anal_retentive"
# End stuff
echo '
/* End of special cases. */'
echo ''
echo '#endif /* !_SYS_VNODE_IF_H_ */'
#
# Redirect stdout to the C file.
#
echo "$0: Creating $out_c" 1>&2
exec > $out_c
# Begin stuff
echo -n "$warning" | sed -e 's/\$//g;s/@/\$/g'
echo ""
echo -n "$copyright"
echo "
#include <sys/cdefs.h>
__KERNEL_RCSID(0, \"\$NetBSD\$\");
"
echo '
/*
* If we have LKM support, always include the non-inline versions for
* LKMs. Otherwise, do it based on the option.
*/
#ifdef LKM
#define VNODE_OP_NOINLINE
#else
#include "opt_vnode_op_noinline.h"
#endif
#include "opt_vnode_lockdebug.h"'
echo '
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/buf.h>
#include <sys/vnode.h>
const struct vnodeop_desc vop_default_desc = {
0,
"default",
0,
NULL,
VDESC_NO_OFFSET,
VDESC_NO_OFFSET,
VDESC_NO_OFFSET,
VDESC_NO_OFFSET,
NULL,
};
'
# Body stuff
sed -e "$sed_prep" $src | $awk '
function do_offset(typematch) {
for (i=0; i<argc; i++) {
if (argtype[i] == typematch) {
printf("\tVOPARG_OFFSETOF(struct %s_args, a_%s),\n",
name, argname[i]);
return i;
};
};
print "\tVDESC_NO_OFFSET,";
return -1;
}
function doit() {
# Define offsets array
printf("\nconst int %s_vp_offsets[] = {\n", name);
for (i=0; i<argc; i++) {
if (argtype[i] == "struct vnode *") {
printf ("\tVOPARG_OFFSETOF(struct %s_args,a_%s),\n",
name, argname[i]);
}
}
print "\tVDESC_NO_OFFSET";
print "};";
# Define F_desc
printf("const struct vnodeop_desc %s_desc = {\n", name);
# offset
printf ("\t%d,\n", vop_offset++);
# printable name
printf ("\t\"%s\",\n", name);
# flags
printf("\t0");
vpnum = 0;
for (i=0; i<argc; i++) {
if (willrele[i]) {
if (willrele[i] == 2) {
word = "UNLOCK";
} else if (willrele[i] == 3) {
word = "PUT";
} else {
word = "RELE";
}
if (argdir[i] ~ /OUT/) {
printf(" | VDESC_VPP_WILL%s", word);
} else {
printf(" | VDESC_VP%s_WILL%s", vpnum, word);
};
vpnum++;
}
}
print ",";
# vp offsets
printf ("\t%s_vp_offsets,\n", name);
# vpp (if any)
do_offset("struct vnode **");
# cred (if any)
do_offset("struct ucred *");
# proc (if any)
do_offset("struct proc *");
# componentname
do_offset("struct componentname *");
# transport layer information
printf ("\tNULL,\n};\n");
# Define function.
printf("#ifdef VNODE_OP_NOINLINE\n");
printf("int\n%s(", toupper(name));
for (i=0; i<argc; i++) {
printf("%s", argname[i]);
if (i < (argc-1)) printf(", ");
}
printf(")\n");
for (i=0; i<argc; i++) {
printf("\t%s %s;\n", argtype[i], argname[i]);
}
printf("{\n\tstruct %s_args a;\n", name);
printf("#ifdef VNODE_LOCKDEBUG\n");
for (i=0; i<argc; i++) {
if (lockstate[i] != -1)
printf("\tint islocked_%s;\n", argname[i]);
}
printf("#endif\n");
printf("\ta.a_desc = VDESC(%s);\n", name);
for (i=0; i<argc; i++) {
printf("\ta.a_%s = %s;\n", argname[i], argname[i]);
if (lockstate[i] != -1) {
printf("#ifdef VNODE_LOCKDEBUG\n");
printf("\tislocked_%s = (%s->v_flag & VLOCKSWORK) ? (VOP_ISLOCKED(%s) == LK_EXCLUSIVE) : %d;\n",
argname[i], argname[i], argname[i], lockstate[i]);
printf("\tif (islocked_%s != %d)\n", argname[i],
lockstate[i]);
printf("\t\tpanic(\"%s: %s: locked %%d, expected %%d\", islocked_%s, %d);\n", name, argname[i], argname[i], lockstate[i]);
printf("#endif\n");
}
}
printf("\treturn (VCALL(%s%s, VOFFSET(%s), &a));\n}\n",
argname[0], arg0special, name);
printf("#endif\n");
}
BEGIN {
printf("\n/* Special cases: */\n");
# start from 1 (vop_default is at 0)
vop_offset=1;
argc=1;
argdir[0]="IN";
argtype[0]="struct buf *";
argname[0]="bp";
lockstate[0] = -1;
arg0special="->b_vp";
willrele[0]=0;
name="vop_bwrite";
doit();
printf("\n/* End of special cases */\n");
arg0special="";
}
'"$awk_parser" | sed -e "$anal_retentive"
# End stuff
echo '
/* End of special cases. */'
# Add the vfs_op_descs array to the C file.
# Begin stuff
echo '
const struct vnodeop_desc * const vfs_op_descs[] = {
&vop_default_desc, /* MUST BE FIRST */
&vop_bwrite_desc, /* XXX: SPECIAL CASE */
'
# Body stuff
sed -e "$sed_prep" $src | $awk '
function doit() {
printf("\t&%s_desc,\n", name);
}
'"$awk_parser"
# End stuff
echo ' NULL
};
'
exit 0
# Local Variables:
# tab-width: 4
# End: