* 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.
This commit is contained in:
dfr 2000-04-08 14:17:18 +00:00
parent 2d18287eb4
commit c9bf4be3c2
26 changed files with 911 additions and 244 deletions

View File

@ -26,6 +26,7 @@
# $FreeBSD$
#
#include <sys/bus.h>
#include <machine/clockvar.h>
INTERFACE clock;

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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

View File

@ -26,6 +26,8 @@
# $FreeBSD$
#
#include <sys/bus.h>
INTERFACE mcclock;
#

View File

@ -26,6 +26,8 @@
# $FreeBSD$
#
#include <sys/bus.h>
INTERFACE iicbb;
#

View File

@ -26,6 +26,8 @@
# $FreeBSD$
#
#include <sys/bus.h>
INTERFACE iicbus;
#

View File

@ -1,5 +1,7 @@
# $FreeBSD$
#include <sys/bus.h>
INTERFACE miibus;
#

View File

@ -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;

View File

@ -26,6 +26,8 @@
# $FreeBSD$
#
#include <sys/bus.h>
INTERFACE pci;
METHOD u_int32_t read_config {

View File

@ -26,6 +26,7 @@
# $FreeBSD$
#
#include <sys/bus.h>
#include <dev/ppbus/ppbconf.h>
INTERFACE ppbus;

View File

@ -26,6 +26,8 @@
# $FreeBSD$
#
#include <sys/bus.h>
INTERFACE smbus;
#

View File

@ -31,6 +31,8 @@
# USB interface description
#
#include <sys/bus.h>
INTERFACE usb;
# The device should start probing for new drivers again

View File

@ -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);

View File

@ -26,9 +26,8 @@
# $FreeBSD$
#
CODE {
#include <sys/bus.h>
#include <isa/isavar.h>
};
INTERFACE isa;

View File

@ -26,6 +26,8 @@
# $FreeBSD$
#
#include <sys/bus.h>
INTERFACE bus;
#

View File

@ -26,6 +26,8 @@
# $FreeBSD$
#
#include <sys/bus.h>
INTERFACE device;
#

481
sys/kern/makeobjops.pl Normal file
View File

@ -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;
}

View File

@ -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);
@ -2317,36 +2183,6 @@ print_method_list(device_method_t *m, int indent)
i, m->desc->name, m->desc->offset));
}
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)
{
@ -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);
}

197
sys/kern/subr_kobj.c Normal file
View File

@ -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);
}

View File

@ -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;

View File

@ -26,6 +26,8 @@
# $FreeBSD$
#
#include <sys/bus.h>
INTERFACE pci;
METHOD u_int32_t read_config {

View File

@ -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.

View File

@ -79,35 +79,16 @@ struct config_device {
struct config_resource *resources;
};
/*
* 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.
*/
@ -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;

164
sys/sys/kobj.h Normal file
View File

@ -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_ */