diff options
author | dfr <dfr@FreeBSD.org> | 2000-04-08 14:17:18 +0000 |
---|---|---|
committer | dfr <dfr@FreeBSD.org> | 2000-04-08 14:17:18 +0000 |
commit | c9bf4be3c29d14ce9b4af20459517870eed8bce9 (patch) | |
tree | b0ea9aab688814aec913835039451900941b1619 /sys | |
parent | 2d18287eb4e48e437d5e7a756df6f20a59c7e176 (diff) | |
download | FreeBSD-src-c9bf4be3c29d14ce9b4af20459517870eed8bce9.zip FreeBSD-src-c9bf4be3c29d14ce9b4af20459517870eed8bce9.tar.gz |
* Factor out the object system from new-bus so that it can be used by
non-device code.
* Re-implement the method dispatch to improve efficiency. The new system
takes about 40ns for a method dispatch on a 300Mhz PII which is only
10ns slower than a direct function call on the same hardware.
This changes the new-bus ABI slightly so make sure you re-compile any
driver modules which you use.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/alpha/alpha/clock_if.m | 1 | ||||
-rw-r--r-- | sys/conf/Makefile.alpha | 4 | ||||
-rw-r--r-- | sys/conf/Makefile.i386 | 4 | ||||
-rw-r--r-- | sys/conf/Makefile.powerpc | 4 | ||||
-rw-r--r-- | sys/conf/files | 1 | ||||
-rw-r--r-- | sys/dev/dec/mcclock_if.m | 2 | ||||
-rw-r--r-- | sys/dev/iicbus/iicbb_if.m | 2 | ||||
-rw-r--r-- | sys/dev/iicbus/iicbus_if.m | 2 | ||||
-rw-r--r-- | sys/dev/mii/miibus_if.m | 2 | ||||
-rw-r--r-- | sys/dev/pci/pci.c | 2 | ||||
-rw-r--r-- | sys/dev/pci/pci_if.m | 2 | ||||
-rw-r--r-- | sys/dev/ppbus/ppbus_if.m | 1 | ||||
-rw-r--r-- | sys/dev/smbus/smbus_if.m | 2 | ||||
-rw-r--r-- | sys/dev/usb/usb_if.m | 2 | ||||
-rw-r--r-- | sys/i386/isa/isa_compat.c | 2 | ||||
-rw-r--r-- | sys/isa/isa_if.m | 3 | ||||
-rw-r--r-- | sys/kern/bus_if.m | 2 | ||||
-rw-r--r-- | sys/kern/device_if.m | 2 | ||||
-rw-r--r-- | sys/kern/makeobjops.pl | 481 | ||||
-rw-r--r-- | sys/kern/subr_bus.c | 220 | ||||
-rw-r--r-- | sys/kern/subr_kobj.c | 197 | ||||
-rw-r--r-- | sys/pci/pci.c | 2 | ||||
-rw-r--r-- | sys/pci/pci_if.m | 2 | ||||
-rw-r--r-- | sys/sys/bus.h | 17 | ||||
-rw-r--r-- | sys/sys/bus_private.h | 32 | ||||
-rw-r--r-- | sys/sys/kobj.h | 164 |
26 files changed, 911 insertions, 244 deletions
diff --git a/sys/alpha/alpha/clock_if.m b/sys/alpha/alpha/clock_if.m index 80c6849..9665135 100644 --- a/sys/alpha/alpha/clock_if.m +++ b/sys/alpha/alpha/clock_if.m @@ -26,6 +26,7 @@ # $FreeBSD$ # +#include <sys/bus.h> #include <machine/clockvar.h> INTERFACE clock; diff --git a/sys/conf/Makefile.alpha b/sys/conf/Makefile.alpha index 6135dc5..a0a114b 100644 --- a/sys/conf/Makefile.alpha +++ b/sys/conf/Makefile.alpha @@ -73,7 +73,7 @@ NORMAL_C_C= ${CC} -c ${CFLAGS} ${PROF} ${.IMPSRC} NORMAL_S= ${CC} -c ${ASM_CFLAGS} ${.IMPSRC} PROFILE_C= ${CC} -c ${CFLAGS} ${.IMPSRC} -NORMAL_M= perl5 $S/kern/makedevops.pl -c $<; \ +NORMAL_M= perl5 $S/kern/makeobjops.pl -c $<; \ ${CC} -c ${CFLAGS} ${PROF} ${.PREFIX}.c GEN_CFILES= $S/$M/$M/genassym.c @@ -127,7 +127,7 @@ ${SYSTEM_OBJS}: vnode_if.h ${BEFORE_DEPEND:M*.h} ${MFILES:T:S/.m$/.h/} .for mfile in ${MFILES} ${mfile:T:S/.m$/.h/}: ${mfile} - perl5 $S/kern/makedevops.pl -h ${mfile} + perl5 $S/kern/makeobjops.pl -h ${mfile} .endfor clean: diff --git a/sys/conf/Makefile.i386 b/sys/conf/Makefile.i386 index c79a7b2..c8054f8 100644 --- a/sys/conf/Makefile.i386 +++ b/sys/conf/Makefile.i386 @@ -73,7 +73,7 @@ NORMAL_C_C= ${CC} -c ${CFLAGS} ${PROF} ${.IMPSRC} NORMAL_S= ${CC} -c ${ASM_CFLAGS} ${.IMPSRC} PROFILE_C= ${CC} -c ${CFLAGS} ${.IMPSRC} -NORMAL_M= perl5 $S/kern/makedevops.pl -c $<; \ +NORMAL_M= perl5 $S/kern/makeobjops.pl -c $<; \ ${CC} -c ${CFLAGS} ${PROF} ${.PREFIX}.c GEN_CFILES= $S/$M/$M/genassym.c @@ -126,7 +126,7 @@ ${SYSTEM_OBJS}: vnode_if.h ${BEFORE_DEPEND:M*.h} ${MFILES:T:S/.m$/.h/} .for mfile in ${MFILES} ${mfile:T:S/.m$/.h/}: ${mfile} - perl5 $S/kern/makedevops.pl -h ${mfile} + perl5 $S/kern/makeobjops.pl -h ${mfile} .endfor clean: diff --git a/sys/conf/Makefile.powerpc b/sys/conf/Makefile.powerpc index c79a7b2..c8054f8 100644 --- a/sys/conf/Makefile.powerpc +++ b/sys/conf/Makefile.powerpc @@ -73,7 +73,7 @@ NORMAL_C_C= ${CC} -c ${CFLAGS} ${PROF} ${.IMPSRC} NORMAL_S= ${CC} -c ${ASM_CFLAGS} ${.IMPSRC} PROFILE_C= ${CC} -c ${CFLAGS} ${.IMPSRC} -NORMAL_M= perl5 $S/kern/makedevops.pl -c $<; \ +NORMAL_M= perl5 $S/kern/makeobjops.pl -c $<; \ ${CC} -c ${CFLAGS} ${PROF} ${.PREFIX}.c GEN_CFILES= $S/$M/$M/genassym.c @@ -126,7 +126,7 @@ ${SYSTEM_OBJS}: vnode_if.h ${BEFORE_DEPEND:M*.h} ${MFILES:T:S/.m$/.h/} .for mfile in ${MFILES} ${mfile:T:S/.m$/.h/}: ${mfile} - perl5 $S/kern/makedevops.pl -h ${mfile} + perl5 $S/kern/makeobjops.pl -h ${mfile} .endfor clean: diff --git a/sys/conf/files b/sys/conf/files index f4615c1..af9baff 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -408,6 +408,7 @@ kern/subr_devstat.c standard kern/subr_disk.c standard kern/subr_diskslice.c standard kern/subr_eventhandler.c standard +kern/subr_kobj.c standard kern/subr_log.c standard kern/subr_module.c standard kern/subr_prf.c standard diff --git a/sys/dev/dec/mcclock_if.m b/sys/dev/dec/mcclock_if.m index 01b8a64..a67caca 100644 --- a/sys/dev/dec/mcclock_if.m +++ b/sys/dev/dec/mcclock_if.m @@ -26,6 +26,8 @@ # $FreeBSD$ # +#include <sys/bus.h> + INTERFACE mcclock; # diff --git a/sys/dev/iicbus/iicbb_if.m b/sys/dev/iicbus/iicbb_if.m index a99bfe8..94baa41 100644 --- a/sys/dev/iicbus/iicbb_if.m +++ b/sys/dev/iicbus/iicbb_if.m @@ -26,6 +26,8 @@ # $FreeBSD$ # +#include <sys/bus.h> + INTERFACE iicbb; # diff --git a/sys/dev/iicbus/iicbus_if.m b/sys/dev/iicbus/iicbus_if.m index ba30c61..eddc022 100644 --- a/sys/dev/iicbus/iicbus_if.m +++ b/sys/dev/iicbus/iicbus_if.m @@ -26,6 +26,8 @@ # $FreeBSD$ # +#include <sys/bus.h> + INTERFACE iicbus; # diff --git a/sys/dev/mii/miibus_if.m b/sys/dev/mii/miibus_if.m index d540964..ee7cd08 100644 --- a/sys/dev/mii/miibus_if.m +++ b/sys/dev/mii/miibus_if.m @@ -1,5 +1,7 @@ # $FreeBSD$ +#include <sys/bus.h> + INTERFACE miibus; # diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index faa813b..574d567 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -940,7 +940,7 @@ compat_pci_handler(module_t mod, int type, void *data) bzero(driver, sizeof(driver_t)); driver->name = dvp->pd_name; driver->methods = pci_compat_methods; - driver->softc = sizeof(struct pci_devinfo *); + driver->size = sizeof(struct pci_devinfo *); driver->priv = dvp; devclass_add_driver(pci_devclass, driver); break; diff --git a/sys/dev/pci/pci_if.m b/sys/dev/pci/pci_if.m index 95b0081..e055f42 100644 --- a/sys/dev/pci/pci_if.m +++ b/sys/dev/pci/pci_if.m @@ -26,6 +26,8 @@ # $FreeBSD$ # +#include <sys/bus.h> + INTERFACE pci; METHOD u_int32_t read_config { diff --git a/sys/dev/ppbus/ppbus_if.m b/sys/dev/ppbus/ppbus_if.m index 00af081..f21dd83 100644 --- a/sys/dev/ppbus/ppbus_if.m +++ b/sys/dev/ppbus/ppbus_if.m @@ -26,6 +26,7 @@ # $FreeBSD$ # +#include <sys/bus.h> #include <dev/ppbus/ppbconf.h> INTERFACE ppbus; diff --git a/sys/dev/smbus/smbus_if.m b/sys/dev/smbus/smbus_if.m index d520c2b..781a159 100644 --- a/sys/dev/smbus/smbus_if.m +++ b/sys/dev/smbus/smbus_if.m @@ -26,6 +26,8 @@ # $FreeBSD$ # +#include <sys/bus.h> + INTERFACE smbus; # diff --git a/sys/dev/usb/usb_if.m b/sys/dev/usb/usb_if.m index e497817..c1b27c8 100644 --- a/sys/dev/usb/usb_if.m +++ b/sys/dev/usb/usb_if.m @@ -31,6 +31,8 @@ # USB interface description # +#include <sys/bus.h> + INTERFACE usb; # The device should start probing for new drivers again diff --git a/sys/i386/isa/isa_compat.c b/sys/i386/isa/isa_compat.c index 3ae09e0..c7617ed 100644 --- a/sys/i386/isa/isa_compat.c +++ b/sys/i386/isa/isa_compat.c @@ -289,7 +289,7 @@ isa_wrap_old_drivers(void) bzero(driver, sizeof(driver_t)); driver->name = op->driver->name; driver->methods = isa_compat_methods; - driver->softc = sizeof(struct isa_device); + driver->size = sizeof(struct isa_device); driver->priv = op; if (op->driver->sensitive_hw) resource_set_int(op->driver->name, -1, "sensitive", 1); diff --git a/sys/isa/isa_if.m b/sys/isa/isa_if.m index 80cc6d2..b93d71c 100644 --- a/sys/isa/isa_if.m +++ b/sys/isa/isa_if.m @@ -26,9 +26,8 @@ # $FreeBSD$ # -CODE { +#include <sys/bus.h> #include <isa/isavar.h> -}; INTERFACE isa; diff --git a/sys/kern/bus_if.m b/sys/kern/bus_if.m index e555462..48d4dc8 100644 --- a/sys/kern/bus_if.m +++ b/sys/kern/bus_if.m @@ -26,6 +26,8 @@ # $FreeBSD$ # +#include <sys/bus.h> + INTERFACE bus; # diff --git a/sys/kern/device_if.m b/sys/kern/device_if.m index 1d6215b..005eb38 100644 --- a/sys/kern/device_if.m +++ b/sys/kern/device_if.m @@ -26,6 +26,8 @@ # $FreeBSD$ # +#include <sys/bus.h> + INTERFACE device; # diff --git a/sys/kern/makeobjops.pl b/sys/kern/makeobjops.pl new file mode 100644 index 0000000..951c9df --- /dev/null +++ b/sys/kern/makeobjops.pl @@ -0,0 +1,481 @@ +#!/usr/bin/perl +# +# Copyright (c) 1992, 1993 +# 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. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. 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. +# +# From @(#)vnode_if.sh 8.1 (Berkeley) 6/10/93 +# From @(#)makedevops.sh 1.1 1998/06/14 13:53:12 dfr Exp $ +# From @(#)makedevops.sh ?.? 1998/10/05 +# From src/sys/kern/makedevops.pl,v 1.12 1999/11/22 14:40:04 n_hibma Exp +# +# $FreeBSD$ + +# +# Script to produce kobj front-end sugar. +# + +$debug = 0; +$cfile = 0; # by default do not produce any file type +$hfile = 0; + +$keepcurrentdir = 1; + +$line_width = 80; + +# Process the command line +# +while ( $arg = shift @ARGV ) { + if ( $arg eq '-c' ) { + warn "Producing .c output files" + if $debug; + $cfile = 1; + } elsif ( $arg eq '-h' ) { + warn "Producing .h output files" + if $debug; + $hfile = 1; + } elsif ( $arg eq '-ch' || $arg eq '-hc' ) { + warn "Producing .c and .h output files" + if $debug; + $cfile = 1; + $hfile = 1; + } elsif ( $arg eq '-d' ) { + $debug = 1; + } elsif ( $arg eq '-p' ) { + warn "Will produce files in original not in current directory" + if $debug; + $keepcurrentdir = 0; + } elsif ( $arg eq '-l' ) { + if ( $line_width = shift @ARGV and $line_width > 0 ) { + warn "Line width set to $line_width" + if $debug; + } else { + die "Please specify a valid line width after -l"; + } + } elsif ( $arg =~ m/\.m$/ ) { + warn "Filename: $arg" + if $debug; + push @filenames, $arg; + } else { + warn "$arg ignored" + if $debug; + } +} + + +# Validate the command line parameters +# +die "usage: $0 [-d] [-p] [-l <nr>] [-c|-h] srcfile +where -c produce only .c files + -h produce only .h files + -p use the path component in the source file for destination dir + -l set line width for output files [80] + -d switch on debugging +" + unless ($cfile or $hfile) + and $#filenames != -1; + +# FIXME should be able to do this more easily +# +$tmpdir = $ENV{'TMPDIR'}; # environment variables +$tmpdir = $ENV{'TMP'} + if !$tmpdir; +$tmpdir = $ENV{'TEMP'} + if !$tmpdir; +$tmpdir = '/tmp' # look for a physical directory + if !$tmpdir and -d '/tmp'; +$tmpdir = '/usr/tmp' + if !$tmpdir and -d '/usr/tmp'; +$tmpdir = '/var/tmp' + if !$tmpdir and -d '/var/tmp'; +$tmpdir = '.' # give up and use current dir + if !$tmpdir; + +foreach $src ( @filenames ) { + # Names of the created files + $ctmpname = "$tmpdir/ctmp.$$"; + $htmpname = "$tmpdir/htmp.$$"; + + ($name, $path, $suffix) = &fileparse($src, '.m'); + $path = '.' + if $keepcurrentdir; + $cfilename="$path/$name.c"; + $hfilename="$path/$name.h"; + + warn "Processing from $src to $cfilename / $hfilename via $ctmpname / $htmpname" + if $debug; + + die "Could not open $src, $!" + if !open SRC, "$src"; + die "Could not open $ctmpname, $!" + if $cfile and !open CFILE, ">$ctmpname"; + die "Could not open $htmpname, $!" + if $hfile and !open HFILE, ">$htmpname"; + + if ($cfile) { + # Produce the header of the C file + # + print CFILE "/*\n"; + print CFILE " * This file is produced automatically.\n"; + print CFILE " * Do not modify anything in here by hand.\n"; + print CFILE " *\n"; + print CFILE " * Created from source file\n"; + print CFILE " * $src\n"; + print CFILE " * with\n"; + print CFILE " * $0\n"; + print CFILE " *\n"; + print CFILE " * See the source file for legal information\n"; + print CFILE " */\n"; + print CFILE "\n"; + print CFILE "#include <sys/param.h>\n"; + print CFILE "#include <sys/queue.h>\n"; + print CFILE "#include <sys/kernel.h>\n"; + print CFILE "#include <sys/kobj.h>\n"; + } + + if ($hfile) { + # Produce the header of the H file + # + print HFILE "/*\n"; + print HFILE " * This file is produced automatically.\n"; + print HFILE " * Do not modify anything in here by hand.\n"; + print HFILE " *\n"; + print HFILE " * Created from source file\n"; + print HFILE " * $src\n"; + print HFILE " * with\n"; + print HFILE " * $0\n"; + print HFILE " *\n"; + print HFILE " * See the source file for legal information\n"; + print HFILE " */\n"; + print HFILE "\n"; + } + + %methods = (); # clear list of methods + @mnames = (); + @defaultmethods = (); + $lineno = 0; + $error = 0; # to signal clean up and gerror setting + + LINE: + while ( $line = <SRC> ) { + $lineno++; + + # take special notice of include directives. + # + if ( $line =~ m/^#\s*include\s+(["<])([^">]+)([">]).*/i ) { + warn "Included file: $1$2" . ($1 eq '<'? '>':'"') + if $debug; + print CFILE "#include $1$2" . ($1 eq '<'? '>':'"') . "\n" + if $cfile; + } + + $line =~ s/#.*//; # remove comments + $line =~ s/^\s+//; # remove leading ... + $line =~ s/\s+$//; # remove trailing whitespace + + if ( $line =~ m/^$/ ) { # skip empty lines + # nop + + } elsif ( $line =~ m/^INTERFACE\s*([^\s;]*)(\s*;?)/i ) { + $intname = $1; + $semicolon = $2; + unless ( $intname =~ m/^[a-z_][a-z0-9_]*$/ ) { + warn $line + if $debug; + warn "$src:$lineno: Invalid interface name '$intname', use [a-z_][a-z0-9_]*"; + $error = 1; + last LINE; + } + + warn "$src:$lineno: semicolon missing at end of line, no problem" + if $semicolon !~ s/;$//; + + warn "Interface $intname" + if $debug; + + print HFILE '#ifndef _'.$intname."_if_h_\n" + if $hfile; + print HFILE '#define _'.$intname."_if_h_\n\n" + if $hfile; + print CFILE '#include "'.$intname.'_if.h"'."\n\n" + if $cfile; + } elsif ( $line =~ m/^CODE\s*{$/i ) { + $code = ""; + $line = <SRC>; + $line =~ m/^(\s*)/; + $indent = $1; # find the indent used + while ( $line !~ m/^}/ ) { + $line =~ s/^$indent//g; # remove the indent + $code .= $line; + $line = <SRC>; + $lineno++ + } + if ($cfile) { + print CFILE "\n".$code."\n"; + } + } elsif ( $line =~ m/^HEADER\s*{$/i ) { + $header = ""; + $line = <SRC>; + $line =~ m/^(\s*)/; + $indent = $1; # find the indent used + while ( $line !~ m/^}/ ) { + $line =~ s/^$indent//g; # remove the indent + $header .= $line; + $line = <SRC>; + $lineno++ + } + if ($hfile) { + print HFILE $header; + } + } elsif ( $line =~ m/^(STATIC|)METHOD/i ) { + # Get the return type function name and delete that from + # the line. What is left is the possibly first function argument + # if it is on the same line. + # + if ( !$intname ) { + warn "$src:$lineno: No interface name defined"; + $error = 1; + last LINE; + } + $line =~ s/^(STATIC|)METHOD\s+([^\{]+?)\s*\{\s*//i; + $static = $1; + @ret = split m/\s+/, $2; + $name = pop @ret; # last element is name of method + $ret = join(" ", @ret); # return type + + warn "Method: name=$name return type=$ret" + if $debug; + + if ( !$name or !$ret ) { + warn $line + if $debug; + warn "$src:$lineno: Invalid method specification"; + $error = 1; + last LINE; + } + + unless ( $name =~ m/^[a-z_][a-z_0-9]*$/ ) { + warn $line + if $debug; + warn "$src:$lineno: Invalid method name '$name', use [a-z_][a-z0-9_]*"; + $error = 1; + last LINE; + } + + if ( defined($methods{$name}) ) { + warn "$src:$lineno: Duplicate method name"; + $error = 1; + last LINE; + } + + $methods{$name} = $name; + push @mnames, $name; + + while ( $line !~ m/}/ and $line .= <SRC> ) { + $lineno++ + } + + $default = ""; + if ( $line !~ s/};?(.*)// ) { # remove first '}' and trailing garbage + # The '}' was not there (the rest is optional), so complain + warn "$src:$lineno: Premature end of file"; + $error = 1; + last LINE; + } + $extra = $1; + if ( $extra =~ /\s*DEFAULT\s*([a-zA-Z_][a-zA-Z_0-9]*)\s*;/ ) { + $default = $1; + } else { + warn "$src:$lineno: Ignored '$1'" # warn about garbage at end of line + if $debug and $1; + } + + # Create a list of variables without the types prepended + # + $line =~ s/^\s+//; # remove leading ... + $line =~ s/\s+$//; # ... and trailing whitespace + $line =~ s/\s+/ /g; # remove double spaces + + @arguments = split m/\s*;\s*/, $line; + @varnames = (); # list of varnames + foreach $argument (@arguments) { + next # skip argument if argument is empty + if !$argument; + + @ar = split m/[*\s]+/, $argument; + if ( $#ar == 0 ) { # only 1 word in argument? + warn "$src:$lineno: no type for '$argument'"; + $error = 1; + last LINE; + } + + push @varnames, $ar[-1]; # last element is name of variable + }; + + warn 'Arguments: ' . join(', ', @arguments) . "\n" + . 'Varnames: ' . join(', ', @varnames) + if $debug; + + $mname = $intname.'_'.$name; # method name + $umname = uc($mname); # uppercase method name + + $arguments = join(", ", @arguments); + $firstvar = $varnames[0]; + $varnames = join(", ", @varnames); + + $default = "0" if $default eq ""; + push @defaultmethods, $default; + + if ($hfile) { + # the method description + print HFILE "extern struct kobjop_desc $mname\_desc;\n"; + # the method typedef + print HFILE &format_line("typedef $ret $mname\_t($arguments);", + $line_width, ', ', + ',',' ' x length("typedef $ret $mname\_t(")) + . "\n"; + } + + if ($cfile) { + # Print out the method desc + print CFILE "struct kobjop_desc $mname\_desc = {\n"; + print CFILE "\t0, (kobjop_t) $default\n"; + print CFILE "};\n\n"; + } + + if ($hfile) { + # Print out the method itself + if (0) { # haven't chosen the format yet + print HFILE "static __inline $ret $umname($varnames)\n"; + print HFILE "\t".join(";\n\t", @arguments).";\n"; + } else { + print HFILE &format_line("static __inline $ret $umname($arguments)", + $line_width, ', ', + ',', ' ' x length("$ret $umname(")) . "\n"; + } + print HFILE "{\n"; + print HFILE "\tkobjop_t _m;\n"; + if ( $static ) { + print HFILE "\tKOBJOPLOOKUP($firstvar->ops,$mname);\n"; + } else { + print HFILE "\tKOBJOPLOOKUP(((kobj_t)$firstvar)->ops,$mname);\n"; + } + print HFILE "\t"; + if ($ret ne 'void') { + print HFILE "return "; + } + print HFILE "(($mname\_t *) _m)($varnames);\n"; + print HFILE "}\n\n"; + } +} else { + warn $line + if $debug; + warn "$src:$lineno: Invalid line encountered"; + $error = 1; + last LINE; +} +} # end LINE + +# print the final '#endif' in the header file +# +print HFILE "#endif /* _".$intname."_if_h_ */\n" + if $hfile; + +close SRC; +close CFILE + if $cfile; +close HFILE + if $hfile; + +if ( !$error ) { + if ($cfile) { + ($rc = system("mv $ctmpname $cfilename")) + and warn "mv $ctmpname $cfilename failed, $rc"; + } + + if ($hfile) { + ($rc = system("mv $htmpname $hfilename")) + and warn "mv $htmpname $hfilename failed, $rc"; + } +} else { + warn 'Output skipped'; + ($rc = system("rm -f $htmpname $ctmpname")) + and warn "rm -f $htmpname $ctmpname failed, $rc"; + $gerror = 1; +} +} + +exit $gerror; + + +sub format_line { + my ($line, $maxlength, $break, $new_end, $new_start) = @_; + my $rline = ""; + + while ( length($line) > $maxlength + and ($i = rindex $line, $break, $maxlength-length($new_end)) != -1 ) { + $rline .= substr($line, 0, $i) . $new_end . "\n"; + $line = $new_start . substr($line, $i+length($break)); + } + + return $rline . $line; +} + +# This routine is a crude replacement for one in File::Basename. We +# cannot use any library code because it fouls up the Perl bootstrap +# when we update a perl version. MarkM + +sub fileparse { + my ($filename, @suffix) = @_; + my ($dir, $name, $type, $i); + + $type = ''; + foreach $i (@suffix) { + if ($filename =~ m|$i$|) { + $filename =~ s|$i$||; + $type = $i; + } + } + if ($filename =~ m|/|) { + $filename =~ m|([^/]*)$|; + $name = $1; + $dir = $filename; + $dir =~ s|$name$||; + } + else { + $dir = ''; + $name = $filename; + } + ($name, $dir, $type); +} + +sub write_interface { + $mcount = $#mnames + 1; +} diff --git a/sys/kern/subr_bus.c b/sys/kern/subr_bus.c index 7492a4b..dbcf4ee 100644 --- a/sys/kern/subr_bus.c +++ b/sys/kern/subr_bus.c @@ -36,6 +36,7 @@ #ifdef DEVICE_SYSCTLS #include <sys/sysctl.h> #endif +#include <sys/kobj.h> #include <sys/bus_private.h> #include <sys/systm.h> #include <machine/bus.h> @@ -56,7 +57,6 @@ MALLOC_DEFINE(M_BUS, "bus", "Bus data structures"); #define indentprintf(p) do { int iJ; printf("."); for (iJ=0; iJ<indent; iJ++) printf(" "); printf p ; } while(0) static void print_method_list(device_method_t *m, int indent); -static void print_device_ops(device_ops_t ops, int indent); static void print_device_short(device_t dev, int indent); static void print_device(device_t dev, int indent); void print_device_tree_short(device_t dev, int indent); @@ -77,7 +77,6 @@ void print_devclass_list(void); #define DEVCLANAME(d) /* nop */ #define print_method_list(m,i) /* nop */ -#define print_device_ops(o,i) /* nop */ #define print_device_short(d,i) /* nop */ #define print_device(d,i) /* nop */ #define print_device_tree_short(d,i) /* nop */ @@ -96,146 +95,11 @@ static void device_register_oids(device_t dev); static void device_unregister_oids(device_t dev); #endif -/* - * Method table handling - */ -static int error_method(void); -static int next_method_offset = 1; - -LIST_HEAD(methodlist, method) methods; -struct method { - LIST_ENTRY(method) link; /* linked list of methods */ - int offset; /* offset in method table */ - int refs; /* count of device_op_desc users */ - devop_t deflt; /* default implementation */ - char* name; /* unique name of method */ +kobj_method_t null_methods[] = { + { 0, 0 } }; -static void -register_method(struct device_op_desc *desc) -{ - struct method* m; - - if (desc->method) { - desc->method->refs++; - return; - } - - /* - * Make sure that desc->deflt is always valid to simplify dispatch. - */ - if (!desc->deflt) - desc->deflt = error_method; - - for (m = LIST_FIRST(&methods); m; m = LIST_NEXT(m, link)) { - if (!strcmp(m->name, desc->name)) { - desc->offset = m->offset; - desc->method = m; - m->refs++; - PDEBUG(("method %p has the same name, %s, with offset %d", - (void *)m, desc->name, desc->offset)); - return; - } - } - - m = (struct method *) malloc(sizeof(struct method) - + strlen(desc->name) + 1, - M_BUS, M_NOWAIT); - if (!m) - panic("register_method: out of memory"); - bzero(m, sizeof(struct method) + strlen(desc->name) + 1); - m->offset = next_method_offset++; - m->refs = 1; - m->deflt = desc->deflt; - m->name = (char*) (m + 1); - strcpy(m->name, desc->name); - LIST_INSERT_HEAD(&methods, m, link); - - desc->offset = m->offset; - desc->method = m; -} - -static void -unregister_method(struct device_op_desc *desc) -{ - struct method *m = desc->method; - m->refs--; - if (m->refs == 0) { - PDEBUG(("method %s, reached refcount 0", desc->name)); - LIST_REMOVE(m, link); - free(m, M_BUS); - desc->method = 0; - } -} - -static int error_method(void) -{ - return ENXIO; -} - -static struct device_ops null_ops = { - 1, - { error_method } -}; - -static void -compile_methods(driver_t *driver) -{ - device_ops_t ops; - struct device_method *m; - struct method *cm; - int i; - - /* - * First register any methods which need it. - */ - for (i = 0, m = driver->methods; m->desc; i++, m++) - register_method(m->desc); - - /* - * Then allocate the compiled op table. - */ - ops = malloc(sizeof(struct device_ops) + (next_method_offset-1) * sizeof(devop_t), - M_BUS, M_NOWAIT); - if (!ops) - panic("compile_methods: out of memory"); - bzero(ops, sizeof(struct device_ops) + (next_method_offset-1) * sizeof(devop_t)); - - ops->maxoffset = next_method_offset; - /* Fill in default methods and then overwrite with driver methods */ - for (i = 0; i < next_method_offset; i++) - ops->methods[i] = error_method; - for (cm = LIST_FIRST(&methods); cm; cm = LIST_NEXT(cm, link)) { - if (cm->deflt) - ops->methods[cm->offset] = cm->deflt; - } - for (i = 0, m = driver->methods; m->desc; i++, m++) - ops->methods[m->desc->offset] = m->func; - PDEBUG(("%s has %d method%s, wasting %d bytes", - DRIVERNAME(driver), i, (i==1?"":"s"), - (next_method_offset-i)*sizeof(devop_t))); - - driver->ops = ops; -} - -static void -free_methods(driver_t *driver) -{ - int i; - struct device_method *m; - - /* - * Unregister any methods which are no longer used. - */ - for (i = 0, m = driver->methods; m->desc; i++, m++) - unregister_method(m->desc); - - /* - * Free memory and clean up. - */ - free(driver->ops, M_BUS); - driver->ops = 0; -} +DEFINE_CLASS(null, null_methods, 0); /* * Devclass implementation @@ -302,8 +166,7 @@ devclass_add_driver(devclass_t dc, driver_t *driver) /* * Compile the driver's methods. */ - if (!driver->ops) - compile_methods(driver); + kobj_class_compile((kobj_class_t) driver); /* * Make sure the devclass which the driver is implementing exists. @@ -379,7 +242,7 @@ devclass_delete_driver(devclass_t busclass, driver_t *driver) driver->refs--; if (driver->refs == 0) - free_methods(driver); + kobj_class_free((kobj_class_t) driver); return 0; } @@ -609,7 +472,7 @@ make_device(device_t parent, const char *name, int unit) dev->parent = parent; TAILQ_INIT(&dev->children); - dev->ops = &null_ops; + kobj_init((kobj_t) dev, &null_class); dev->driver = NULL; dev->devclass = NULL; dev->unit = unit; @@ -630,6 +493,8 @@ make_device(device_t parent, const char *name, int unit) dev->state = DS_NOTPRESENT; + kobj_init((kobj_t) dev, &null_class); + return dev; } @@ -1112,18 +977,19 @@ device_set_driver(device_t dev, driver_t *driver) free(dev->softc, M_BUS); dev->softc = NULL; } - dev->ops = &null_ops; + kobj_delete((kobj_t) dev, 0); dev->driver = driver; if (driver) { - dev->ops = driver->ops; - dev->softc = malloc(driver->softc, M_BUS, M_NOWAIT); + kobj_init((kobj_t) dev, (kobj_class_t) driver); + dev->softc = malloc(driver->size, M_BUS, M_NOWAIT); if (!dev->softc) { - dev->ops = &null_ops; + kobj_init((kobj_t) dev, &null_class); dev->driver = NULL; return ENOMEM; } - bzero(dev->softc, driver->softc); - } + bzero(dev->softc, driver->size); + } else + kobj_init((kobj_t) dev, &null_class); return 0; } @@ -2180,17 +2046,17 @@ root_setup_intr(device_t dev, device_t child, driver_intr_t *intr, void *arg, panic("root_setup_intr"); } -static device_method_t root_methods[] = { +static kobj_method_t root_methods[] = { /* Device interface */ - DEVMETHOD(device_shutdown, bus_generic_shutdown), - DEVMETHOD(device_suspend, bus_generic_suspend), - DEVMETHOD(device_resume, bus_generic_resume), + KOBJMETHOD(device_shutdown, bus_generic_shutdown), + KOBJMETHOD(device_suspend, bus_generic_suspend), + KOBJMETHOD(device_resume, bus_generic_resume), /* Bus interface */ - DEVMETHOD(bus_print_child, root_print_child), - DEVMETHOD(bus_read_ivar, bus_generic_read_ivar), - DEVMETHOD(bus_write_ivar, bus_generic_write_ivar), - DEVMETHOD(bus_setup_intr, root_setup_intr), + KOBJMETHOD(bus_print_child, root_print_child), + KOBJMETHOD(bus_read_ivar, bus_generic_read_ivar), + KOBJMETHOD(bus_write_ivar, bus_generic_write_ivar), + KOBJMETHOD(bus_setup_intr, root_setup_intr), { 0, 0 } }; @@ -2209,10 +2075,10 @@ root_bus_module_handler(module_t mod, int what, void* arg) { switch (what) { case MOD_LOAD: - compile_methods(&root_driver); + kobj_class_compile((kobj_class_t) &root_driver); root_bus = make_device(NULL, "root", 0); root_bus->desc = "System root bus"; - root_bus->ops = root_driver.ops; + kobj_init((kobj_t) root_bus, (kobj_class_t) &root_driver); root_bus->driver = &root_driver; root_bus->state = DS_ATTACHED; root_devclass = devclass_find_internal("root", FALSE); @@ -2318,36 +2184,6 @@ print_method_list(device_method_t *m, int indent) } static void -print_device_ops(device_ops_t ops, int indent) -{ - int i; - int count = 0; - - if (!ops) - return; - - /* we present a list of the methods that are pointing to the - * error_method, but ignore the 0'th elements; it is always - * error_method. - */ - for (i = 1; i < ops->maxoffset; i++) { - if (ops->methods[i] == error_method) { - if (count == 0) - indentprintf(("error_method:")); - printf(" %d", i); - count++; - } - } - if (count) - printf("\n"); - - indentprintf(("(%d method%s, %d valid, %d error_method%s)\n", - ops->maxoffset-1, (ops->maxoffset-1 == 1? "":"s"), - ops->maxoffset-1-count, - count, (count == 1? "":"'s"))); -} - -static void print_device_short(device_t dev, int indent) { if (!dev) @@ -2376,8 +2212,6 @@ print_device(device_t dev, int indent) indentprintf(("Parent:\n")); print_device_short(dev->parent, indent+1); - indentprintf(("Methods:\n")); - print_device_ops(dev->ops, indent+1); indentprintf(("Driver:\n")); print_driver_short(dev->driver, indent+1); indentprintf(("Devclass:\n")); @@ -2435,8 +2269,6 @@ print_driver(driver_t *driver, int indent) print_driver_short(driver, indent); indentprintf(("Methods:\n")); print_method_list(driver->methods, indent+1); - indentprintf(("Operations:\n")); - print_device_ops(driver->ops, indent+1); } diff --git a/sys/kern/subr_kobj.c b/sys/kern/subr_kobj.c new file mode 100644 index 0000000..9a2eafb --- /dev/null +++ b/sys/kern/subr_kobj.c @@ -0,0 +1,197 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/errno.h> +#ifndef TEST +#include <sys/systm.h> +#endif +#include <sys/kobj.h> + +#ifdef TEST +#include "usertest.h" +#endif + +static MALLOC_DEFINE(M_KOBJ, "kobj", "Kernel object structures"); + +#ifdef KOBJ_STATS + +#include <sys/sysctl.h> + +int kobj_lookup_hits; +int kobj_lookup_misses; + +SYSCTL_INT(_kern, OID_AUTO, kobj_hits, CTLFLAG_RD, + &kobj_lookup_hits, 0, "") +SYSCTL_INT(_kern, OID_AUTO, kobj_misses, CTLFLAG_RD, + &kobj_lookup_misses, 0, "") + +#endif + +static int kobj_next_id = 1; + +static int +kobj_error_method(void) +{ + return ENXIO; +} + +static void +kobj_register_method(struct kobjop_desc *desc) +{ + if (desc->id == 0) + desc->id = kobj_next_id++; +} + +static void +kobj_unregister_method(struct kobjop_desc *desc) +{ +} + +void +kobj_class_compile(kobj_class_t cls) +{ + kobj_ops_t ops; + kobj_method_t *m; + int i; + + /* + * Don't do anything if we are already compiled. + */ + if (cls->ops) + return; + + /* + * First register any methods which need it. + */ + for (i = 0, m = cls->methods; m->desc; i++, m++) + kobj_register_method(m->desc); + + /* + * Then allocate the compiled op table. + */ + ops = malloc(sizeof(struct kobj_ops), M_KOBJ, M_NOWAIT); + if (!ops) + panic("kobj_compile_methods: out of memory"); + bzero(ops, sizeof(struct kobj_ops)); + ops->cls = cls; + cls->ops = ops; +} + +void +kobj_lookup_method(kobj_method_t *methods, + kobj_method_t *ce, + kobjop_desc_t desc) +{ + ce->desc = desc; + for (; methods && methods->desc; methods++) { + if (methods->desc == desc) { + ce->func = methods->func; + return; + } + } + if (desc->deflt) + ce->func = desc->deflt; + else + ce->func = kobj_error_method; + return; +} + +void +kobj_class_free(kobj_class_t cls) +{ + int i; + kobj_method_t *m; + + /* + * Unregister any methods which are no longer used. + */ + for (i = 0, m = cls->methods; m->desc; i++, m++) + kobj_unregister_method(m->desc); + + /* + * Free memory and clean up. + */ + free(cls->ops, M_KOBJ); + cls->ops = 0; +} + +kobj_t +kobj_create(kobj_class_t cls, + struct malloc_type *mtype, + int mflags) +{ + kobj_t obj; + + /* + * Allocate and initialise the new object. + */ + obj = malloc(cls->size, mtype, mflags); + if (!obj) + return 0; + bzero(obj, cls->size); + kobj_init(obj, cls); + + return obj; +} + +void +kobj_init(kobj_t obj, kobj_class_t cls) +{ + /* + * Consider compiling the class' method table. + */ + if (!cls->ops) + kobj_class_compile(cls); + + obj->ops = cls->ops; + cls->instances++; +} + +void +kobj_delete(kobj_t obj, struct malloc_type *mtype) +{ + kobj_class_t cls = obj->ops->cls; + + /* + * Consider freeing the compiled method table for the class + * after its last instance is deleted. As an optimisation, we + * should defer this for a short while to avoid thrashing. + */ + cls->instances--; + if (!cls->instances) + kobj_class_free(cls); + + obj->ops = 0; + if (mtype) + free(obj, mtype); +} diff --git a/sys/pci/pci.c b/sys/pci/pci.c index faa813b..574d567 100644 --- a/sys/pci/pci.c +++ b/sys/pci/pci.c @@ -940,7 +940,7 @@ compat_pci_handler(module_t mod, int type, void *data) bzero(driver, sizeof(driver_t)); driver->name = dvp->pd_name; driver->methods = pci_compat_methods; - driver->softc = sizeof(struct pci_devinfo *); + driver->size = sizeof(struct pci_devinfo *); driver->priv = dvp; devclass_add_driver(pci_devclass, driver); break; diff --git a/sys/pci/pci_if.m b/sys/pci/pci_if.m index 95b0081..e055f42 100644 --- a/sys/pci/pci_if.m +++ b/sys/pci/pci_if.m @@ -26,6 +26,8 @@ # $FreeBSD$ # +#include <sys/bus.h> + INTERFACE pci; METHOD u_int32_t read_config { diff --git a/sys/sys/bus.h b/sys/sys/bus.h index 77bd903..f1ed037 100644 --- a/sys/sys/bus.h +++ b/sys/sys/bus.h @@ -32,16 +32,15 @@ #ifdef _KERNEL #include <sys/queue.h> +#include <sys/kobj.h> /* * Forward declarations */ typedef struct device *device_t; typedef struct driver driver_t; -typedef struct device_method device_method_t; typedef struct devclass *devclass_t; -typedef struct device_ops *device_ops_t; -typedef struct device_op_desc *device_op_desc_t; +#define device_method_t kobj_method_t typedef void driver_intr_t(void*); @@ -63,17 +62,9 @@ enum intr_type { typedef int (*devop_t)(void); -struct device_method { - device_op_desc_t desc; - devop_t func; -}; - struct driver { - const char *name; /* driver name */ - device_method_t *methods; /* method table */ - size_t softc; /* size of device softc struct */ + KOBJ_CLASS_FIELDS; void *priv; /* driver private data */ - device_ops_t ops; /* compiled method table */ int refs; /* # devclasses containing driver */ }; @@ -301,7 +292,7 @@ int resource_count(void); /* * Shorthand for constructing method tables. */ -#define DEVMETHOD(NAME, FUNC) { &NAME##_desc, (devop_t) FUNC } +#define DEVMETHOD KOBJMETHOD /* * Some common device interfaces. diff --git a/sys/sys/bus_private.h b/sys/sys/bus_private.h index 485e921..547892b 100644 --- a/sys/sys/bus_private.h +++ b/sys/sys/bus_private.h @@ -80,35 +80,16 @@ struct config_device { }; /* - * Compiled device methods. - */ -struct device_ops { - int maxoffset; - devop_t methods[1]; -}; - -/* - * Helpers for device method wrappers. - */ -#define DEVOPDESC(OP) (&OP##_##desc) - -#define DEVOPS(DEV) (DEV->ops) -#define DEVOPMETH(DEV, OP) \ - ((DEVOPDESC(OP)->offset >= DEVOPS(DEV)->maxoffset) \ - ? DEVOPDESC(OP)->deflt \ - : DEVOPS(DEV)->methods[DEVOPDESC(OP)->offset]) - -#define DRVOPS(DRV) ((struct device_ops *)DRV->ops) -#define DRVOPMETH(DRV, OP) \ - ((DEVOPDESC(OP)->offset >= DRVOPS(DRV)->maxoffset) \ - ? DEVOPDESC(OP)->deflt \ - : DRVOPS(DRV)->methods[DEVOPDESC(OP)->offset]) - -/* * Implementation of device. */ struct device { /* + * A device is a kernel object. The first field must be the + * current ops table for the object. + */ + KOBJ_FIELDS; + + /* * Device hierarchy. */ TAILQ_ENTRY(device) link; /* list of devices in parent */ @@ -118,7 +99,6 @@ struct device { /* * Details of this device. */ - device_ops_t ops; driver_t *driver; devclass_t devclass; /* device class which we are in */ int unit; diff --git a/sys/sys/kobj.h b/sys/sys/kobj.h new file mode 100644 index 0000000..b0a20d0 --- /dev/null +++ b/sys/sys/kobj.h @@ -0,0 +1,164 @@ +#define KOBJ_STATS +/*- + * Copyright (c) 2000 Doug Rabson + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef _SYS_KOBJ_H_ +#define _SYS_KOBJ_H_ + +/* + * Forward declarations + */ +typedef struct kobj *kobj_t; +typedef struct kobj_class *kobj_class_t; +typedef struct kobj_method kobj_method_t; +typedef int (*kobjop_t)(void); +typedef struct kobj_ops *kobj_ops_t; +typedef struct kobjop_desc *kobjop_desc_t; +struct malloc_type; + +struct kobj_method { + kobjop_desc_t desc; + kobjop_t func; +}; + +/* + * A class is simply a method table and a sizeof value. When the first + * instance of the class is created, the method table will be compiled + * into a form more suited to efficient method dispatch. This compiled + * method table is always the first field of the object. + */ +#define KOBJ_CLASS_FIELDS \ + const char *name; /* class name */ \ + kobj_method_t *methods; /* method table */ \ + size_t size; /* object size */ \ + u_int instances; /* instance count */ \ + kobj_ops_t ops /* compiled method table */ + +struct kobj_class { + KOBJ_CLASS_FIELDS; +}; + +/* + * Implementation of kobj. + */ +#define KOBJ_FIELDS \ + kobj_ops_t ops; + +struct kobj { + KOBJ_FIELDS; +}; + +/* + * The ops table is used as a cache of results from kobj_lookup_method(). + */ + +#define KOBJ_CACHE_SIZE 256 + +struct kobj_ops { + kobj_method_t cache[KOBJ_CACHE_SIZE]; + kobj_class_t cls; +}; + +struct kobjop_desc { + unsigned int id; /* unique ID */ + kobjop_t deflt; /* default implementation */ +}; + +/* + * Shorthand for constructing method tables. + */ +#define KOBJMETHOD(NAME, FUNC) { &NAME##_desc, (kobjop_t) FUNC } + +#define DEFINE_CLASS(name, methods, size) \ + \ +struct kobj_class name ## _class = { \ + #name, methods, size \ +} + +/* + * Compile the method table in a class. + */ +void kobj_class_compile(kobj_class_t cls); + +/* + * Free the compiled method table in a class. + */ +void kobj_class_free(kobj_class_t cls); + +/* + * Allocate memory for and initalise a new object. + */ +kobj_t kobj_create(kobj_class_t cls, + struct malloc_type *mtype, + int mflags); + +/* + * Initialise a pre-allocated object. + */ +void kobj_init(kobj_t obj, kobj_class_t cls); + +/* + * Delete an object. If mtype is non-zero, free the memory. + */ +void kobj_delete(kobj_t obj, struct malloc_type *mtype); + +/* + * Maintain stats on hits/misses in lookup caches. + */ +#ifdef KOBJ_STATS +extern int kobj_lookup_hits; +extern int kobj_lookup_misses; +#define KOBJOPHIT do { kobj_lookup_hits++; } while (0) +#define KOBJOPMISS do { kobj_lookup_misses++; } while (0) +#else +#define KOBJOPHIT do { } while (0) +#define KOBJOPMISS do { } while (0) +#endif + +/* + * Lookup the method in the cache and if it isn't there look it up the + * slow way. + */ +#define KOBJOPLOOKUP(OPS,OP) do { \ + kobjop_desc_t _desc = &OP##_##desc; \ + kobj_method_t *_ce = \ + &OPS->cache[_desc->id & (KOBJ_CACHE_SIZE-1)]; \ + if (_ce->desc != _desc) { \ + KOBJOPMISS; \ + kobj_lookup_method(OPS->cls->methods, _ce, _desc); \ + } else { \ + KOBJOPHIT; \ + } \ + _m = _ce->func; \ +} while(0) + +void kobj_lookup_method(kobj_method_t *methods, + kobj_method_t *ce, + kobjop_desc_t desc); + +#endif /* !_SYS_KOBJ_H_ */ |