summaryrefslogtreecommitdiffstats
path: root/sys/kern
diff options
context:
space:
mode:
authordfr <dfr@FreeBSD.org>2000-04-08 14:17:18 +0000
committerdfr <dfr@FreeBSD.org>2000-04-08 14:17:18 +0000
commitc9bf4be3c29d14ce9b4af20459517870eed8bce9 (patch)
treeb0ea9aab688814aec913835039451900941b1619 /sys/kern
parent2d18287eb4e48e437d5e7a756df6f20a59c7e176 (diff)
downloadFreeBSD-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/kern')
-rw-r--r--sys/kern/bus_if.m2
-rw-r--r--sys/kern/device_if.m2
-rw-r--r--sys/kern/makeobjops.pl481
-rw-r--r--sys/kern/subr_bus.c220
-rw-r--r--sys/kern/subr_kobj.c197
5 files changed, 708 insertions, 194 deletions
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);
+}
OpenPOWER on IntegriCloud