lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20090409163052.32740.24910.stgit@dev.haskins.net>
Date:	Thu, 09 Apr 2009 12:30:52 -0400
From:	Gregory Haskins <ghaskins@...ell.com>
To:	linux-kernel@...r.kernel.org
Cc:	agraf@...e.de, pmullaney@...ell.com, pmorreale@...ell.com,
	anthony@...emonkey.ws, rusty@...tcorp.com.au,
	netdev@...r.kernel.org, kvm@...r.kernel.org, avi@...hat.com,
	bhutchings@...arflare.com, andi@...stfloor.org, gregkh@...e.de,
	herber@...dor.apana.org.au, chrisw@...s-sol.org,
	shemminger@...tta.com
Subject: [RFC PATCH v2 02/19] vbus: add virtual-bus definitions

See Documentation/vbus.txt for details

Signed-off-by: Gregory Haskins <ghaskins@...ell.com>
---

 Documentation/vbus.txt      |  386 +++++++++++++++++++++++++++++
 arch/x86/Kconfig            |    2 
 fs/proc/base.c              |   96 +++++++
 include/linux/sched.h       |    4 
 include/linux/vbus.h        |  147 +++++++++++
 include/linux/vbus_device.h |  417 ++++++++++++++++++++++++++++++++
 kernel/Makefile             |    1 
 kernel/exit.c               |    2 
 kernel/fork.c               |    2 
 kernel/vbus/Kconfig         |   14 +
 kernel/vbus/Makefile        |    1 
 kernel/vbus/attribute.c     |   52 ++++
 kernel/vbus/config.c        |  275 +++++++++++++++++++++
 kernel/vbus/core.c          |  567 +++++++++++++++++++++++++++++++++++++++++++
 kernel/vbus/devclass.c      |  124 +++++++++
 kernel/vbus/map.c           |   72 +++++
 kernel/vbus/map.h           |   41 +++
 kernel/vbus/vbus.h          |  116 +++++++++
 18 files changed, 2319 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/vbus.txt
 create mode 100644 include/linux/vbus.h
 create mode 100644 include/linux/vbus_device.h
 create mode 100644 kernel/vbus/Kconfig
 create mode 100644 kernel/vbus/Makefile
 create mode 100644 kernel/vbus/attribute.c
 create mode 100644 kernel/vbus/config.c
 create mode 100644 kernel/vbus/core.c
 create mode 100644 kernel/vbus/devclass.c
 create mode 100644 kernel/vbus/map.c
 create mode 100644 kernel/vbus/map.h
 create mode 100644 kernel/vbus/vbus.h

diff --git a/Documentation/vbus.txt b/Documentation/vbus.txt
new file mode 100644
index 0000000..e8a05da
--- /dev/null
+++ b/Documentation/vbus.txt
@@ -0,0 +1,386 @@
+
+Virtual-Bus:
+======================
+Author: Gregory Haskins <ghaskins@...ell.com>
+
+
+
+
+What is it?
+--------------------
+
+Virtual-Bus is a kernel based IO resource container technology.  It is modeled
+on a concept similar to the Linux Device-Model (LDM), where we have buses,
+devices, and drivers as the primary actors.  However, VBUS has several
+distinctions when contrasted with LDM:
+
+  1) "Busses" in LDM are relatively static and global to the kernel (e.g.
+     "PCI", "USB", etc).  VBUS buses are arbitrarily created and destroyed
+     dynamically, and are not globally visible.  Instead they are defined as
+     visible only to a specific subset of the system (the contained context).
+  2) "Devices" in LDM are typically tangible physical (or sometimes logical)
+     devices.  VBUS devices are purely software abstractions (which may or
+     may not have one or more physical devices behind them).  Devices may
+     also be arbitrarily created or destroyed by software/administrative action
+     as opposed to by a hardware discovery mechanism.
+  3) "Drivers" in LDM sit within the same kernel context as the busses and
+     devices they interact with.  VBUS drivers live in a foreign
+     context (such as userspace, or a virtual-machine guest).
+
+The idea is that a vbus is created to contain access to some IO services.
+Virtual devices are then instantiated and linked to a bus to grant access to
+drivers actively present on the bus.  Drivers will only have visibility to
+devices present on their respective bus, and nothing else.
+
+Virtual devices are defined by modules which register a deviceclass with the
+system.  A deviceclass simply represents a type of device that _may_ be
+instantiated into a device, should an administrator wish to do so.  Once
+this has happened, the device may be associated with one or more buses where
+it will become visible to all clients of those respective buses.
+
+Why do we need this?
+----------------------
+
+There are various reasons why such a construct may be useful.  One of the
+most interesting use cases is for virtualization, such as KVM.  Hypervisors
+today provide virtualized IO resources to a guest, but this is often at a cost
+in both latency and throughput compared to bare metal performance.  Utilizing
+para-virtual resources instead of emulated devices helps to mitigate this
+penalty, but even these techniques to date have not fully realized the
+potential of the underlying bare-metal hardware.
+
+Some of the performance differential is unavoidable just given the extra
+processing that occurs due to the deeper stack (guest+host).  However, some of
+this overhead is a direct result of the rather indirect path most hypervisors
+use to route IO.  For instance, KVM uses PIO faults from the guest to trigger
+a guest->host-kernel->host-userspace->host-kernel sequence of events.
+Contrast this to a typical userspace application on the host which must only
+traverse app->kernel for most IO.
+
+The fact is that the linux kernel is already great at managing access to IO
+resources.  Therefore, if you have a hypervisor that is based on the linux
+kernel, is there some way that we can allow the hypervisor to manage IO
+directly instead of forcing this convoluted path?
+
+The short answer is: "not yet" ;)
+
+In order to use such a concept, we need some new facilties.  For one, we
+need to be able to define containers with their corresponding access-control so
+that guests do not have unmitigated access to anything they wish.  Second,
+we also need to define some forms of memory access that is uniform in the face
+of various clients (e.g. "copy_to_user()" cannot be assumed to work for, say,
+a KVM vcpu context).  Lastly, we need to provide access to these resources in
+a way that makes sense for the application, such as asynchronous communication
+paths and minimizing context switches.
+
+So we introduce VBUS as a framework to provide such facilities.  The net
+result is a *substantial* reduction in IO overhead, even when compared to
+state of the art para-virtualization techniques (such as virtio-net).
+
+How do I use it?
+------------------------
+
+There are two components to utilizing a virtual-bus.  One is the
+administrative function (creating and configuring a bus and its devices).  The
+other is the consumption of the resources on the bus by a client (e.g. a
+virtual machine, or a userspace application).  The former occurs on the host
+kernel by means of interacting with various special filesystems (e.g. sysfs,
+configfs, etc).  The latter occurs by means of a "vbus connector" which must
+be developed specifically to bridge a particular environment.  To date, we
+have developed such connectors for host-userspace and kvm-guests.  Conceivably
+we could develop other connectors as needs arise (e.g. lguest, xen,
+guest-userspace, etc).  This document deals with the administrative interface.
+Details about developing a connector are out of scope for this document.
+
+Interacting with vbus
+------------------------
+
+The first step is to enable virtual-bus support (CONFIG_VBUS) as well as any
+desired vbus-device modules (e.g. CONFIG_VBUS_VENETTAP), and ensure that your
+environment mounts both sysfs and configfs somewhere in the filesystem.  This
+document will assume they are mounted to /sys and /config, respectively.
+
+VBUS will create a top-level directory "vbus" in each of the two respective
+filesystems.  At boot-up, they will look like the following:
+
+/sys/vbus/
+|-- deviceclass
+|-- devices
+|-- instances
+`-- version
+
+/config/vbus/
+|-- devices
+`-- instances
+
+Following their respective roles, /config/vbus is for userspace to manage the
+lifetime of some number of objects/attributes.  This is in contrast to
+/sys/vbus which is a reflection of objects managed by the kernel.  It is
+assumed the reader is already familiar with these two facilities, so we will
+not go into depth about their general operation.  Suffice to say that vbus
+consists of objects that are managed both by userspace and the kernel.
+Modification of objects via /config/vbus will typically be reflected in the
+/sys/vbus area.
+
+It all starts with a deviceclass
+--------------------------------
+
+Before you can do anything useful with vbus, you need some registered
+deviceclasses.  A deviceclass provides the implementation of a specific type
+of virtual device.  A deviceclass will typically be registered by loading a
+kernel-module.  Once loaded, the available device types are enumerated under
+/sys/vbus/deviceclass.  For example, we will load our "venet-tap" module,
+which provides network services:
+
+# modprobe venet-tap
+# tree /sys/vbus
+/sys/vbus
+|-- deviceclass
+|   `-- venet-tap
+|-- devices
+|-- instances
+`-- version
+
+An administrative agent should be able to enumerate /sys/vbus/deviceclass to
+determine what services are available on a given platform.
+
+Create the container
+-------------------
+
+The next step is to create a new container.  In vbus, this comes in the form
+of a vbus-instance and it is created by a simple "mkdir" in the
+/config/vbus/instances area.  The only requirement is that the instance is
+given a host-wide unique name.  This may be some kind of association to the
+application (e.g. the unique VM GUID) or it can be arbitrary.  For the
+purposes of example, we will let $(uuidgen) generate a random UUID for us.
+
+# mkdir /config/vbus/instances/$(uuidgen)
+# tree /sys/vbus/
+/sys/vbus/
+|-- deviceclass
+|   `-- venet-tap
+|-- devices
+|-- instances
+|   `-- beb4df8f-7483-4028-b3f7-767512e2a18c
+|       |-- devices
+|       `-- members
+`-- version
+
+So we can see that we have now created a vbus called
+
+               "beb4df8f-7483-4028-b3f7-767512e2a18c"
+
+in the /config area, and it was immediately reflected in the
+/sys/vbus/instances area as well (with a few subobjects of its own: "devices"
+and "members").  The "devices" object denotes any devices that are present on
+the bus (in this case: none).  Likewise, "members" denotes the pids of any
+tasks that are members of the bus (in this case: none).  We will come back to
+this later.  For now, we move on to the next step
+
+Create a device instance
+------------------------
+
+Devices are instantiated by again utilizing the /config/vbus configfs area.
+At first you may suspect that devices are created as subordinate objects of a
+bus/container instance, but you would be mistaken.  Devices are actually
+root-level objects in vbus specifically to allow greater flexibility in the
+association of a device.  For instance, it may be desirable to have a single
+device that spans multiple VMs (consider an ethernet switch, or a shared disk
+for a cluster).  Therefore, device lifecycles are managed by creating/deleting
+objects in /config/vbus/devices.
+
+Note: Creating a device instance is actually a two step process:  We need to
+give the device instance a unique name, and we also need to give it a specific
+device type.  It is hard to express both parameters using standard filesystem
+operations like mkdir, so the design decision was made to require performing
+the operation in two steps.
+
+Our first step is to create a unique instance.  We will again utilize
+$(uuidgen) to yield an arbitrary name.  Any name will suffice as long as it is
+unqie on this particular host.
+
+# mkdir /config/vbus/devices/$(uuidgen)
+# tree /sys/vbus
+/sys/vbus
+|-- deviceclass
+|   `-- venet-tap
+|-- devices
+|   `-- 6a1aff24-5dc0-4aea-9c35-435daef90e55
+|       `-- interfaces
+|-- instances
+|   `-- beb4df8f-7483-4028-b3f7-767512e2a18c
+|       |-- devices
+|       `-- members
+`-- version
+
+At this point we have created a partial instance, since we have not yet
+assigned a type to the device.  Even so, we can see that some state has
+changed under /sys/vbus/devices.  We now have an instance named
+
+	      	 6a1aff24-5dc0-4aea-9c35-435daef90e55
+
+and it has a single subordinate object: "interfaces".  This object in
+particular is provided by the infrastructure, though do note that a
+deviceclass may also provide its own attributes/objects once it is created.
+
+We will go ahead and give this device a type to complete its construction.  We
+do this by setting the /config/vbus/devices/$devname/type attribute with a
+valid deviceclass type:
+
+# echo foo > /config/vbus/devices/6a1aff24-5dc0-4aea-9c35-435daef90e55/type
+bash: echo: write error: No such file or directory
+
+Oops!  What happened?  "foo" is not a valid deviceclass.  We need to consult
+the /sys/vbus/deviceclass area to find out what our options are:
+
+# tree /sys/vbus/deviceclass/
+/sys/vbus/deviceclass/
+`-- venet-tap
+
+Lets try again:
+
+# echo venet-tap > /config/vbus/devices/6a1aff24-5dc0-4aea-9c35-435daef90e55/type
+# tree /sys/vbus/
+/sys/vbus/
+|-- deviceclass
+|   `-- venet-tap
+|-- devices
+|   `-- 6a1aff24-5dc0-4aea-9c35-435daef90e55
+|       |-- class -> ../../deviceclass/venet-tap
+|       |-- client_mac
+|       |-- enabled
+|       |-- host_mac
+|       |-- ifname
+|       `-- interfaces
+|-- instances
+|   `-- beb4df8f-7483-4028-b3f7-767512e2a18c
+|       |-- devices
+|       `-- members
+`-- version
+
+Ok, that looks better.  And note that /sys/vbus/devices now has some more
+subordinate objects.  Most of those were registered when the venet-tap
+deviceclass was given a chance to create an instance of itself.  Those
+attributes are a property of the venet-tap and therefore are out of scope
+for this document.  Please see the documentation that accompanies a particular
+module for more details.
+
+Put the device on the bus
+-------------------------
+
+The next administrative step is to associate our new device with our bus.
+This is accomplished using a symbolic link from the bus instance to our device
+instance.
+
+ln -s /config/vbus/devices/6a1aff24-5dc0-4aea-9c35-435daef90e55/ /config/vbus/instances/beb4df8f-7483-4028-b3f7-767512e2a18c/
+# tree /sys/vbus/
+/sys/vbus/
+|-- deviceclass
+|   `-- venet-tap
+|-- devices
+|   `-- 6a1aff24-5dc0-4aea-9c35-435daef90e55
+|       |-- class -> ../../deviceclass/venet-tap
+|       |-- client_mac
+|       |-- enabled
+|       |-- host_mac
+|       |-- ifname
+|       `-- interfaces
+|           `-- 0 -> ../../../instances/beb4df8f-7483-4028-b3f7-767512e2a18c/devices/0
+|-- instances
+|   `-- beb4df8f-7483-4028-b3f7-767512e2a18c
+|       |-- devices
+|       |   `-- 0
+|       |       |-- device -> ../../../../devices/6a1aff24-5dc0-4aea-9c35-435daef90e55
+|       |       `-- type
+|       `-- members
+`-- version
+
+We can now see that the device indicates that it has an interface registered
+to a bus:
+
+/sys/vbus/devices/6a1aff24-5dc0-4aea-9c35-435daef90e55/interfaces/
+`-- 0 -> ../../../instances/beb4df8f-7483-4028-b3f7-767512e2a18c/devices/0
+
+Likewise, we can see that the bus has a device listed (id = "0"):
+
+/sys/vbus/instances/beb4df8f-7483-4028-b3f7-767512e2a18c/devices/
+`-- 0
+    |-- device -> ../../../../devices/6a1aff24-5dc0-4aea-9c35-435daef90e55
+    `-- type
+
+At this point, our container is ready for use.  However, it currently has 0
+members, so lets fix that
+
+Add some members
+--------------------
+
+Membership is controlled by an attribute: /proc/$pid/vbus.  A pid can only be
+a member of one (or zero) busses at a time.  To establish membership, we set
+the name of the bus, like so:
+
+# echo beb4df8f-7483-4028-b3f7-767512e2a18c > /proc/self/vbus
+# tree /sys/vbus/
+/sys/vbus/
+|-- deviceclass
+|   `-- venet-tap
+|-- devices
+|   `-- 6a1aff24-5dc0-4aea-9c35-435daef90e55
+|       |-- class -> ../../deviceclass/venet-tap
+|       |-- client_mac
+|       |-- enabled
+|       |-- host_mac
+|       |-- ifname
+|       `-- interfaces
+|           `-- 0 -> ../../../instances/beb4df8f-7483-4028-b3f7-767512e2a18c/devices/0
+|-- instances
+|   `-- beb4df8f-7483-4028-b3f7-767512e2a18c
+|       |-- devices
+|       |   `-- 0
+|       |       |-- device -> ../../../../devices/6a1aff24-5dc0-4aea-9c35-435daef90e55
+|       |       `-- type
+|       `-- members
+|           |-- 4382
+|           `-- 4588
+`-- version
+
+Woah!  Why are there two members?  VBUS membership is inherited by forked
+tasks.  Therefore 4382 is the pid of our shell (which we set via /proc/self),
+and 4588 is the pid of the forked/exec'ed "tree" process.  This property can
+be useful for having things like qemu set up the bus and then forking each
+vcpu which will inherit access.
+
+At this point, we are ready to roll.  Pid 4382 has access to a virtual-bus
+namespace with one device, id=0.  Its type is:
+
+# cat /sys/vbus/instances/beb4df8f-7483-4028-b3f7-767512e2a18c/devices/0/type
+virtual-ethernet
+
+"virtual-ethernet"?  Why is it not "venet-tap"?  Device-classes are allowed to
+register their interfaces under an id that is not required to be the same as
+their deviceclass.  This supports device polymorphism.   For instance,
+consider that an interface "virtual-ethernet" may provide basic 802.x packet
+exchange.  However, we could have various implementations of a device that
+supports the 802.x interface, while having various implementations behind
+them.
+
+For instance, "venet-tap" might act like a tuntap module, while
+"venet-loopback" would loop packets back and "venet-switch" would form a
+layer-2 domain among the participating guests.  All three modules would
+presumably support the same basic 802.x interface, yet all three have
+completely different implementations.
+
+Drivers on this particular bus would see this instance id=0 as a type
+"virtual-ethernet" even though the underlying implementation happens to be a
+tap device.  This means a single driver that supports the protocol advertised
+by the "virtual-ethernet" type would be able to support the plethera of
+available device types that we may wish to create.
+
+Teardown:
+---------------
+
+We can descontruct a vbus container doing pretty much the opposite of what we
+did to create it.  Echo "0" into /proc/self/vbus, rm the symlink between the
+bus and device, and rmdir the bus and device objects.  Once that is done, we
+can even rmmod the venet-tap module.  Note that the infrastructure will
+maintain a module-ref while it is configured in a container, so be sure to
+completely tear down the vbus/device before trying this.
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index bc2fbad..3fca247 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1939,6 +1939,8 @@ source "drivers/pcmcia/Kconfig"
 
 source "drivers/pci/hotplug/Kconfig"
 
+source "kernel/vbus/Kconfig"
+
 endmenu
 
 
diff --git a/fs/proc/base.c b/fs/proc/base.c
index beaa0ce..03993fb 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -80,6 +80,7 @@
 #include <linux/oom.h>
 #include <linux/elf.h>
 #include <linux/pid_namespace.h>
+#include <linux/vbus.h>
 #include "internal.h"
 
 /* NOTE:
@@ -1065,6 +1066,98 @@ static const struct file_operations proc_oom_adjust_operations = {
 	.write		= oom_adjust_write,
 };
 
+#ifdef CONFIG_VBUS
+
+static ssize_t vbus_read(struct file *file, char __user *buf,
+			 size_t count, loff_t *ppos)
+{
+	struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode);
+	struct vbus *vbus;
+	const char *name;
+	char buffer[256];
+	size_t len;
+
+	if (!task)
+		return -ESRCH;
+
+	vbus = task_vbus_get(task);
+
+	put_task_struct(task);
+
+	name = vbus_name(vbus);
+
+	len = snprintf(buffer, sizeof(buffer), "%s\n", name ? name : "<none>");
+
+	vbus_put(vbus);
+
+	return simple_read_from_buffer(buf, count, ppos, buffer, len);
+}
+
+static ssize_t vbus_write(struct file *file, const char __user *buf,
+			  size_t count, loff_t *ppos)
+{
+	struct task_struct *task;
+	struct vbus *vbus = NULL;
+	char buffer[256];
+	int disable = 0;
+
+	memset(buffer, 0, sizeof(buffer));
+	if (count > sizeof(buffer) - 1)
+		count = sizeof(buffer) - 1;
+	if (copy_from_user(buffer, buf, count))
+		return -EFAULT;
+
+	if (buffer[count-1] == '\n')
+		buffer[count-1] = 0;
+
+	task = get_proc_task(file->f_path.dentry->d_inode);
+	if (!task)
+		return -ESRCH;
+
+	if (!capable(CAP_SYS_ADMIN)) {
+		put_task_struct(task);
+		return -EACCES;
+	}
+
+	if (strcmp(buffer, "0") == 0)
+		disable = 1;
+	else
+		vbus = vbus_find(buffer);
+
+	if (disable || vbus)
+		task_vbus_disassociate(task);
+
+	if (vbus) {
+		int ret = vbus_associate(vbus, task);
+
+		if (ret < 0)
+			printk(KERN_ERR \
+			       "vbus: could not associate %s/%d with bus %s",
+			       task->comm, task->pid, vbus_name(vbus));
+		else
+			rcu_assign_pointer(task->vbus, vbus);
+
+		vbus_put(vbus); /* Counter the vbus_find() */
+	} else if (!disable) {
+		put_task_struct(task);
+		return -ENOENT;
+	}
+
+	put_task_struct(task);
+
+	if (count == sizeof(buffer)-1)
+		return -EIO;
+
+	return count;
+}
+
+static const struct file_operations proc_vbus_operations = {
+	.read		= vbus_read,
+	.write		= vbus_write,
+};
+
+#endif /* CONFIG_VBUS */
+
 #ifdef CONFIG_AUDITSYSCALL
 #define TMPBUFLEN 21
 static ssize_t proc_loginuid_read(struct file * file, char __user * buf,
@@ -2556,6 +2649,9 @@ static const struct pid_entry tgid_base_stuff[] = {
 #ifdef CONFIG_TASK_IO_ACCOUNTING
 	INF("io",	S_IRUGO, proc_tgid_io_accounting),
 #endif
+#ifdef CONFIG_VBUS
+	REG("vbus", S_IRUGO|S_IWUSR, proc_vbus_operations),
+#endif
 };
 
 static int proc_tgid_base_readdir(struct file * filp,
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 011db2f..cd2f9b1 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -97,6 +97,7 @@ struct futex_pi_state;
 struct robust_list_head;
 struct bio;
 struct bts_tracer;
+struct vbus;
 
 /*
  * List of flags we want to share for kernel threads,
@@ -1329,6 +1330,9 @@ struct task_struct {
 	unsigned int lockdep_recursion;
 	struct held_lock held_locks[MAX_LOCK_DEPTH];
 #endif
+#ifdef CONFIG_VBUS
+	struct vbus *vbus;
+#endif
 
 /* journalling filesystem info */
 	void *journal_info;
diff --git a/include/linux/vbus.h b/include/linux/vbus.h
new file mode 100644
index 0000000..5f0566c
--- /dev/null
+++ b/include/linux/vbus.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2009 Novell.  All Rights Reserved.
+ *
+ * Virtual-Bus
+ *
+ * Author:
+ *      Gregory Haskins <ghaskins@...ell.com>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _LINUX_VBUS_H
+#define _LINUX_VBUS_H
+
+#ifdef CONFIG_VBUS
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/rcupdate.h>
+#include <linux/vbus_device.h>
+
+struct vbus;
+struct task_struct;
+
+/**
+ * vbus_associate() - associate a task with a vbus
+ * @vbus:      The bus context to associate with
+ * @p:         The task to associate
+ *
+ * This function adds a task as a member of a vbus.  Tasks must be members
+ * of a bus before they are allowed to use its resources.  Tasks may only
+ * associate with a single bus at a time.
+ *
+ * Note: children inherit any association present at fork().
+ *
+ * Returns: success = 0, <0 = ERRNO
+ *
+ **/
+int vbus_associate(struct vbus *vbus, struct task_struct *p);
+
+/**
+ * vbus_disassociate() - disassociate a task with a vbus
+ * @vbus:      The bus context to disassociate with
+ * @p:         The task to disassociate
+ *
+ * This function removes a task as a member of a vbus.
+ *
+ * Returns: success = 0, <0 = ERRNO
+ *
+ **/
+int vbus_disassociate(struct vbus *vbus, struct task_struct *p);
+
+struct vbus *vbus_get(struct vbus *);
+void vbus_put(struct vbus *);
+
+/**
+ * vbus_name() - returns the name of a bus
+ * @vbus:      The bus context
+ *
+ * Returns: (char *) name of bus
+ *
+ **/
+const char *vbus_name(struct vbus *vbus);
+
+/**
+ * vbus_find() - retreives a vbus pointer from its name
+ * @name:      The name of the bus to find
+ *
+ * Returns: NULL = failure, non-null = (vbus *)bus-pointer
+ *
+ **/
+struct vbus *vbus_find(const char *name);
+
+/**
+ * task_vbus_get() - retreives an associated vbus pointer from a task
+ * @p:         The task context
+ *
+ * Safely retreives a pointer to an associated (if any) vbus from a task
+ *
+ * Returns: NULL = no association, non-null = (vbus *)bus-pointer
+ *
+ **/
+static inline struct vbus *task_vbus_get(struct task_struct *p)
+{
+	struct vbus *vbus;
+
+	rcu_read_lock();
+	vbus = rcu_dereference(p->vbus);
+	if (vbus)
+		vbus_get(vbus);
+	rcu_read_unlock();
+
+	return vbus;
+}
+
+/**
+ * fork_vbus() - Helper function to handle associated task forking
+ * @p:         The task context
+ *
+ **/
+static inline void fork_vbus(struct task_struct *p)
+{
+	struct vbus *vbus = task_vbus_get(p);
+
+	if (vbus) {
+		BUG_ON(vbus_associate(vbus, p) < 0);
+		vbus_put(vbus);
+	}
+}
+
+/**
+ * task_vbus_disassociate() - Helper function to handle disassociating tasks
+ * @p:         The task context
+ *
+ **/
+static inline void task_vbus_disassociate(struct task_struct *p)
+{
+	struct vbus *vbus = task_vbus_get(p);
+
+	if (vbus) {
+		rcu_assign_pointer(p->vbus, NULL);
+		synchronize_rcu();
+
+		vbus_disassociate(vbus, p);
+		vbus_put(vbus);
+	}
+}
+
+#else /* CONFIG_VBUS */
+
+#define fork_vbus(p) do { } while (0)
+#define task_vbus_disassociate(p) do { } while (0)
+
+#endif /* CONFIG_VBUS */
+
+#endif /* _LINUX_VBUS_H */
diff --git a/include/linux/vbus_device.h b/include/linux/vbus_device.h
new file mode 100644
index 0000000..f73cd86
--- /dev/null
+++ b/include/linux/vbus_device.h
@@ -0,0 +1,417 @@
+/*
+ * VBUS device models
+ *
+ * Copyright 2009 Novell.  All Rights Reserved.
+ *
+ * Author:
+ *      Gregory Haskins <ghaskins@...ell.com>
+ *
+ * This file deals primarily with the definitions for interfacing a virtual
+ * device model to a virtual bus.  In a nutshell, a devclass begets a device,
+ * which begets a device_interface, which begets a connection.
+ *
+ * devclass
+ * -------
+ *
+ * To develop a vbus device, it all starts with a devclass.  You must register
+ * a devclass using vbus_devclass_register().  Each registered devclass is
+ * enumerated under /sys/vbus/deviceclass.
+ *
+ * In of itself, a devclass doesnt do much.  It is just an object factory for
+ * a device whose lifetime is managed by userspace.  When userspace decides
+ * it would like to create an instance of a particular devclass, the
+ * devclass::create() callback is invoked (registered as part of the ops
+ * structure during vbus_devclass_register()).  How and when userspace decides
+ * to do this is beyond the scope of this document.  Please see:
+ *
+ *                         Documentation/vbus.txt
+ *
+ * for more details.
+ *
+ * device
+ * -------
+ *
+ * A vbus device is created by a particular devclass during the invokation
+ * of its devclass::create() callback.  A device is initially created without
+ * any association with a bus.  One or more buses may attempt to connect to
+ * a device (controlled, again, by userspace).  When this occurs, a
+ * device::bus_connect() callback is invoked.
+ *
+ * This bus_connect() callback gives the device a chance to decide if it will
+ * accept the connection, and if so, to register its interfaces.  Most devices
+ * will likely only allow a connection to one bus.  Therefore, they may return
+ * -EBUSY another bus is already connected.
+ *
+ * If the device accepts the connection, it should register one of more
+ * interfaces with the bus using vbus_device_interface_register().  Most
+ * devices will only support one interface, and therefore will only invoke
+ * this method once.  However, some more elaborate devices may have multiple
+ * functions, or abstracted topologies.  Therefore they may opt at their own
+ * discretion to register more than one interface.  The interfaces do not need
+ * to be uniform in type.
+ *
+ * device_interface
+ * -------------------
+ *
+ * The purpose of an interface is two fold: 1) advertise a particular ABI
+ * for communcation to a driver, 2) handle the initial connection of a driver.
+ *
+ * As such, a device_interface has a string "type" (which is akin to the
+ * abi type that this interface supports, like a PCI-ID).  It also sports
+ * an interface::open() method.
+ *
+ * The interface::open callback is invoked whenever a driver attempts to
+ * connect to this device.  The device implements its own policy regarding
+ * whether it accepts multiple connections or not.  Most devices will likely
+ * only accept one connection at a time, and therefore will return -EBUSY if
+ * subsequent attempts are made.
+ *
+ * However, if successful, the interface::open() should return a
+ * vbus_connection object
+ *
+ * connections
+ * -----------
+ *
+ * A connection represents an interface that is succesfully opened.  It will
+ * remain in an active state as long as the client retains the connection.
+ * The connection::release() method is invoked if the client should die,
+ * restart, or explicitly close the connection.  The device-model should use
+ * this release() callback as the indication to clean up any resources
+ * associated with a particular connection such as allocated queues, etc.
+ *
+ * ---
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _LINUX_VBUS_DEVICE_H
+#define _LINUX_VBUS_DEVICE_H
+
+#include <linux/module.h>
+#include <linux/configfs.h>
+#include <linux/rbtree.h>
+#include <linux/shm_signal.h>
+#include <linux/vbus.h>
+#include <asm/atomic.h>
+
+struct vbus_device_interface;
+struct vbus_connection;
+struct vbus_device;
+struct vbus_devclass;
+struct vbus_memctx;
+
+/*
+ * ----------------------
+ * devclass
+ * ----------------------
+ */
+struct vbus_devclass_ops {
+	int (*create)(struct vbus_devclass *dc,
+		      struct vbus_device **dev);
+	void (*release)(struct vbus_devclass *dc);
+};
+
+struct vbus_devclass {
+	const char *name;
+	struct vbus_devclass_ops *ops;
+	struct rb_node node;
+	struct kobject kobj;
+	struct module *owner;
+};
+
+/**
+ * vbus_devclass_register() - register a devclass with the system
+ * @devclass:   The devclass context to register
+ *
+ * Establishes a new device-class for consumption.  Registered device-classes
+ * are enumerated under /sys/vbus/deviceclass.  For more details, please see
+ * Documentation/vbus*
+ *
+ * Returns: success = 0, <0 = ERRNO
+ *
+ **/
+int vbus_devclass_register(struct vbus_devclass *devclass);
+
+/**
+ * vbus_devclass_unregister() - unregister a devclass with the system
+ * @devclass:   The devclass context to unregister
+ *
+ * Removes a devclass from the system
+ *
+ * Returns: success = 0, <0 = ERRNO
+ *
+ **/
+int vbus_devclass_unregister(struct vbus_devclass *devclass);
+
+/**
+ * vbus_devclass_get() - acquire a devclass context reference
+ * @devclass:      devclass context
+ *
+ **/
+static inline struct vbus_devclass *
+vbus_devclass_get(struct vbus_devclass *devclass)
+{
+	if (!try_module_get(devclass->owner))
+		return NULL;
+
+	kobject_get(&devclass->kobj);
+	return devclass;
+}
+
+/**
+ * vbus_devclass_put() - release a devclass context reference
+ * @devclass:      devclass context
+ *
+ **/
+static inline void
+vbus_devclass_put(struct vbus_devclass *devclass)
+{
+	kobject_put(&devclass->kobj);
+	module_put(devclass->owner);
+}
+
+/*
+ * ----------------------
+ * device
+ * ----------------------
+ */
+struct vbus_device_attribute {
+	struct attribute attr;
+	ssize_t (*show)(struct vbus_device *dev,
+			struct vbus_device_attribute *attr,
+			char *buf);
+	ssize_t (*store)(struct vbus_device *dev,
+			 struct vbus_device_attribute *attr,
+			 const char *buf, size_t count);
+};
+
+struct vbus_device_ops {
+	int (*bus_connect)(struct vbus_device *dev, struct vbus *vbus);
+	int (*bus_disconnect)(struct vbus_device *dev, struct vbus *vbus);
+	void (*release)(struct vbus_device *dev);
+};
+
+struct vbus_device {
+	const char *type;
+	struct vbus_device_ops *ops;
+	struct attribute_group *attrs;
+	struct kobject *kobj;
+};
+
+/*
+ * ----------------------
+ * device_interface
+ * ----------------------
+ */
+struct vbus_device_interface_ops {
+	int (*open)(struct vbus_device_interface *intf,
+		    struct vbus_memctx *ctx,
+		    int version,
+		    struct vbus_connection **conn);
+	void (*release)(struct vbus_device_interface *intf);
+};
+
+struct vbus_device_interface {
+	const char *name;
+	const char *type;
+	struct vbus_device_interface_ops *ops;
+	unsigned long id;
+	struct vbus_device *dev;
+	struct vbus *vbus;
+	struct rb_node node;
+	struct kobject kobj;
+};
+
+/**
+ * vbus_device_interface_register() - register an interface with a bus
+ * @dev:        The device context of the caller
+ * @vbus:       The bus context to register with
+ * @intf:       The interface context to register
+ *
+ * This function is invoked (usually in the context of a device::bus_connect()
+ * callback) to register a interface on a bus.  We make this an explicit
+ * operation instead of implicit on the bus_connect() to facilitate devices
+ * that may present multiple interfaces to a bus.  In those cases, a device
+ * may invoke this function multiple times (one per supported interface).
+ *
+ * Returns: success = 0, <0 = ERRNO
+ *
+ **/
+int vbus_device_interface_register(struct vbus_device *dev,
+				   struct vbus *vbus,
+				   struct vbus_device_interface *intf);
+
+/**
+ * vbus_device_interface_unregister() - unregister an interface with a bus
+ * @intf:       The interface context to unregister
+ *
+ * This function is the converse of interface_register.  It is typically
+ * invoked in the context of a device::bus_disconnect().
+ *
+ * Returns: success = 0, <0 = ERRNO
+ *
+ **/
+int vbus_device_interface_unregister(struct vbus_device_interface *intf);
+
+/*
+ * ----------------------
+ * memory context
+ * ----------------------
+ */
+struct vbus_memctx_ops {
+	unsigned long (*copy_to)(struct vbus_memctx *ctx,
+				 void *dst,
+				 const void *src,
+				 unsigned long len);
+	unsigned long (*copy_from)(struct vbus_memctx *ctx,
+				   void *dst,
+				   const void *src,
+				   unsigned long len);
+	void (*release)(struct vbus_memctx *ctx);
+};
+
+struct vbus_memctx {
+	atomic_t refs;
+	struct vbus_memctx_ops *ops;
+};
+
+static inline void
+vbus_memctx_init(struct vbus_memctx *ctx, struct vbus_memctx_ops *ops)
+{
+	memset(ctx, 0, sizeof(*ctx));
+	atomic_set(&ctx->refs, 1);
+	ctx->ops = ops;
+}
+
+#define VBUS_MEMCTX_INIT(_ops) {                                   \
+	.refs = ATOMIC_INIT(1),                                    \
+	.ops = _ops,                                               \
+}
+
+static inline void
+vbus_memctx_get(struct vbus_memctx *ctx)
+{
+	atomic_inc(&ctx->refs);
+}
+
+static inline void
+vbus_memctx_put(struct vbus_memctx *ctx)
+{
+	if (atomic_dec_and_test(&ctx->refs))
+		ctx->ops->release(ctx);
+}
+
+/*
+ * ----------------------
+ * memory context
+ * ----------------------
+ */
+struct vbus_shm;
+
+struct vbus_shm_ops {
+	void (*release)(struct vbus_shm *shm);
+};
+
+struct vbus_shm {
+	atomic_t refs;
+	struct vbus_shm_ops *ops;
+	void                *ptr;
+	size_t               len;
+};
+
+static inline void
+vbus_shm_init(struct vbus_shm *shm, struct vbus_shm_ops *ops,
+	      void *ptr, size_t len)
+{
+	memset(shm, 0, sizeof(*shm));
+	atomic_set(&shm->refs, 1);
+	shm->ops = ops;
+	shm->ptr = ptr;
+	shm->len = len;
+}
+
+static inline void
+vbus_shm_get(struct vbus_shm *shm)
+{
+	atomic_inc(&shm->refs);
+}
+
+static inline void
+vbus_shm_put(struct vbus_shm *shm)
+{
+	if (atomic_dec_and_test(&shm->refs))
+		shm->ops->release(shm);
+}
+
+/*
+ * ----------------------
+ * connection
+ * ----------------------
+ */
+struct vbus_connection_ops {
+	int (*call)(struct vbus_connection *conn,
+		    unsigned long func,
+		    void *data,
+		    unsigned long len,
+		    unsigned long flags);
+	int (*shm)(struct vbus_connection *conn,
+		   unsigned long id,
+		   struct vbus_shm *shm,
+		   struct shm_signal *signal,
+		   unsigned long flags);
+	void (*close)(struct vbus_connection *conn);
+	void (*release)(struct vbus_connection *conn);
+};
+
+struct vbus_connection {
+	atomic_t refs;
+	struct vbus_connection_ops *ops;
+};
+
+/**
+ * vbus_connection_init() - initialize a vbus_connection
+ * @conn:       connection context
+ * @ops:        ops structure to assign to context
+ *
+ **/
+static inline void vbus_connection_init(struct vbus_connection *conn,
+					struct vbus_connection_ops *ops)
+{
+	memset(conn, 0, sizeof(*conn));
+	atomic_set(&conn->refs, 1);
+	conn->ops = ops;
+}
+
+/**
+ * vbus_connection_get() - acquire a connection context reference
+ * @conn:       connection context
+ *
+ **/
+static inline void vbus_connection_get(struct vbus_connection *conn)
+{
+	atomic_inc(&conn->refs);
+}
+
+/**
+ * vbus_connection_put() - release a connection context reference
+ * @conn:       connection context
+ *
+ **/
+static inline void vbus_connection_put(struct vbus_connection *conn)
+{
+	if (atomic_dec_and_test(&conn->refs))
+		conn->ops->release(conn);
+}
+
+#endif /* _LINUX_VBUS_DEVICE_H */
diff --git a/kernel/Makefile b/kernel/Makefile
index e4791b3..99a98a7 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -93,6 +93,7 @@ obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o
 obj-$(CONFIG_FUNCTION_TRACER) += trace/
 obj-$(CONFIG_TRACING) += trace/
 obj-$(CONFIG_SMP) += sched_cpupri.o
+obj-$(CONFIG_VBUS) += vbus/
 
 ifneq ($(CONFIG_SCHED_OMIT_FRAME_POINTER),y)
 # According to Alan Modra <alan@...uxcare.com.au>, the -fno-omit-frame-pointer is
diff --git a/kernel/exit.c b/kernel/exit.c
index efd30cc..8736de6 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -48,6 +48,7 @@
 #include <linux/tracehook.h>
 #include <linux/init_task.h>
 #include <trace/sched.h>
+#include <linux/vbus.h>
 
 #include <asm/uaccess.h>
 #include <asm/unistd.h>
@@ -1081,6 +1082,7 @@ NORET_TYPE void do_exit(long code)
 	check_stack_usage();
 	exit_thread();
 	cgroup_exit(tsk, 1);
+	task_vbus_disassociate(tsk);
 
 	if (group_dead && tsk->signal->leader)
 		disassociate_ctty(1);
diff --git a/kernel/fork.c b/kernel/fork.c
index 4854c2c..5536053 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -61,6 +61,7 @@
 #include <linux/proc_fs.h>
 #include <linux/blkdev.h>
 #include <trace/sched.h>
+#include <linux/vbus.h>
 
 #include <asm/pgtable.h>
 #include <asm/pgalloc.h>
@@ -1274,6 +1275,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
 	write_unlock_irq(&tasklist_lock);
 	proc_fork_connector(p);
 	cgroup_post_fork(p);
+	fork_vbus(p);
 	return p;
 
 bad_fork_free_graph:
diff --git a/kernel/vbus/Kconfig b/kernel/vbus/Kconfig
new file mode 100644
index 0000000..f2b92f5
--- /dev/null
+++ b/kernel/vbus/Kconfig
@@ -0,0 +1,14 @@
+#
+# Virtual-Bus (VBus) configuration
+#
+
+config VBUS
+       bool "Virtual Bus"
+       select CONFIGFS_FS
+       select SHM_SIGNAL
+       default n
+       help
+        Provides a mechansism for declaring virtual-bus objects and binding
+	various tasks and devices which reside on the bus.
+
+	If unsure, say N
diff --git a/kernel/vbus/Makefile b/kernel/vbus/Makefile
new file mode 100644
index 0000000..367f65b
--- /dev/null
+++ b/kernel/vbus/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_VBUS) += core.o devclass.o config.o attribute.o map.o
diff --git a/kernel/vbus/attribute.c b/kernel/vbus/attribute.c
new file mode 100644
index 0000000..3928228
--- /dev/null
+++ b/kernel/vbus/attribute.c
@@ -0,0 +1,52 @@
+#include <linux/vbus.h>
+#include <linux/uaccess.h>
+#include <linux/kobject.h>
+#include <linux/kallsyms.h>
+
+#include "vbus.h"
+
+static struct vbus_device_attribute *to_vattr(struct attribute *attr)
+{
+	return container_of(attr, struct vbus_device_attribute, attr);
+}
+
+static struct vbus_devshell *to_devshell(struct kobject *kobj)
+{
+	return container_of(kobj, struct vbus_devshell, kobj);
+}
+
+static ssize_t _dev_attr_show(struct kobject *kobj, struct attribute *attr,
+			     char *buf)
+{
+	struct vbus_devshell *ds = to_devshell(kobj);
+	struct vbus_device_attribute *vattr = to_vattr(attr);
+	ssize_t ret = -EIO;
+
+	if (vattr->show)
+		ret = vattr->show(ds->dev, vattr, buf);
+
+	if (ret >= (ssize_t)PAGE_SIZE) {
+		print_symbol("vbus_attr_show: %s returned bad count\n",
+				(unsigned long)vattr->show);
+	}
+
+	return ret;
+}
+
+static ssize_t _dev_attr_store(struct kobject *kobj, struct attribute *attr,
+			       const char *buf, size_t count)
+{
+	struct vbus_devshell *ds = to_devshell(kobj);
+	struct vbus_device_attribute *vattr = to_vattr(attr);
+	ssize_t ret = -EIO;
+
+	if (vattr->store)
+		ret = vattr->store(ds->dev, vattr, buf, count);
+
+	return ret;
+}
+
+struct sysfs_ops vbus_dev_attr_ops = {
+	.show	= _dev_attr_show,
+	.store	= _dev_attr_store,
+};
diff --git a/kernel/vbus/config.c b/kernel/vbus/config.c
new file mode 100644
index 0000000..a40dbf1
--- /dev/null
+++ b/kernel/vbus/config.c
@@ -0,0 +1,275 @@
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <linux/vbus.h>
+#include <linux/configfs.h>
+
+#include "vbus.h"
+
+static struct config_item_type perms_type = {
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct vbus *to_vbus(struct config_group *group)
+{
+	return group ? container_of(group, struct vbus, ci.group) : NULL;
+}
+
+static struct vbus *item_to_vbus(struct config_item *item)
+{
+	return to_vbus(to_config_group(item));
+}
+
+static struct vbus_devshell *to_devshell(struct config_group *group)
+{
+	return group ? container_of(group, struct vbus_devshell, ci_group)
+		: NULL;
+}
+
+static struct vbus_devshell *to_vbus_devshell(struct config_item *item)
+{
+	return to_devshell(to_config_group(item));
+}
+
+static int
+device_bus_connect(struct config_item *src, struct config_item *target)
+{
+	struct vbus *vbus = item_to_vbus(src);
+	struct vbus_devshell *ds;
+
+	/* We only allow connections to devices */
+	if (target->ci_parent != &vbus_root.devices.ci_group.cg_item)
+		return -EINVAL;
+
+	ds = to_vbus_devshell(target);
+	BUG_ON(!ds);
+
+	if (!ds->dev)
+		return -EINVAL;
+
+	return ds->dev->ops->bus_connect(ds->dev, vbus);
+}
+
+static int
+device_bus_disconnect(struct config_item *src, struct config_item *target)
+{
+	struct vbus *vbus = item_to_vbus(src);
+	struct vbus_devshell *ds;
+
+	ds = to_vbus_devshell(target);
+	BUG_ON(!ds);
+
+	if (!ds->dev)
+		return -EINVAL;
+
+	return ds->dev->ops->bus_disconnect(ds->dev, vbus);
+}
+
+struct configfs_item_operations bus_ops = {
+	.allow_link = device_bus_connect,
+	.drop_link = device_bus_disconnect,
+};
+
+static struct config_item_type bus_type = {
+	.ct_item_ops    = &bus_ops,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct config_group *bus_create(struct config_group *group,
+				       const char *name)
+{
+	struct vbus *bus = NULL;
+	int ret;
+
+	ret = vbus_create(name, &bus);
+	if (ret < 0)
+		return ERR_PTR(ret);
+
+	config_group_init_type_name(&bus->ci.group, name, &bus_type);
+	bus->ci.group.default_groups = bus->ci.defgroups;
+	bus->ci.group.default_groups[0] = &bus->ci.perms;
+	bus->ci.group.default_groups[1] = NULL;
+
+	config_group_init_type_name(&bus->ci.perms, "perms", &perms_type);
+
+	return &bus->ci.group;
+}
+
+static void bus_destroy(struct config_group *group, struct config_item *item)
+{
+	struct vbus *vbus = item_to_vbus(item);
+
+	vbus_put(vbus);
+}
+
+static struct configfs_group_operations buses_ops = {
+	.make_group	= bus_create,
+	.drop_item      = bus_destroy,
+};
+
+static struct config_item_type buses_type = {
+	.ct_group_ops	= &buses_ops,
+	.ct_owner	= THIS_MODULE,
+};
+
+CONFIGFS_ATTR_STRUCT(vbus_devshell);
+#define DEVSHELL_ATTR(_name, _mode, _show, _store)	\
+struct vbus_devshell_attribute vbus_devshell_attr_##_name = \
+    __CONFIGFS_ATTR(_name, _mode, _show, _store)
+
+static ssize_t devshell_type_read(struct vbus_devshell *ds, char *page)
+{
+	if (ds->dev)
+		return sprintf(page, "%s\n", ds->dev->type);
+	else
+		return sprintf(page, "\n");
+}
+
+static ssize_t devshell_type_write(struct vbus_devshell *ds, const char *page,
+				   size_t count)
+{
+	struct vbus_devclass *dc;
+	struct vbus_device *dev;
+	char name[256];
+	int ret;
+
+	/*
+	 * The device-type can only be set once, and then it is permenent.
+	 * The admin should delete the device-shell if they want to create
+	 * a new type
+	 */
+	if (ds->dev)
+		return -EINVAL;
+
+	if (count > sizeof(name))
+		return -EINVAL;
+
+	strcpy(name, page);
+	if (name[count-1] == '\n')
+		name[count-1] = 0;
+
+	dc = vbus_devclass_find(name);
+	if (!dc)
+		return -ENOENT;
+
+	ret = dc->ops->create(dc, &dev);
+	if (ret < 0) {
+		vbus_devclass_put(dc);
+		return ret;
+	}
+
+	ds->dev = dev;
+	ds->dc = dc;
+	dev->kobj = &ds->kobj;
+
+	ret = vbus_devshell_type_set(ds);
+	if (ret < 0) {
+		vbus_devclass_put(dc);
+		return ret;
+	}
+
+	return count;
+}
+
+DEVSHELL_ATTR(type, S_IRUGO | S_IWUSR, devshell_type_read,
+	    devshell_type_write);
+
+static struct configfs_attribute *devshell_attrs[] = {
+	&vbus_devshell_attr_type.attr,
+	NULL,
+};
+
+CONFIGFS_ATTR_OPS(vbus_devshell);
+static struct configfs_item_operations devshell_item_ops = {
+	.show_attribute		= vbus_devshell_attr_show,
+	.store_attribute	= vbus_devshell_attr_store,
+};
+
+static struct config_item_type devshell_type = {
+	.ct_item_ops	= &devshell_item_ops,
+	.ct_attrs	= devshell_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct config_group *devshell_create(struct config_group *group,
+					    const char *name)
+{
+	struct vbus_devshell *ds = NULL;
+	int ret;
+
+	ret = vbus_devshell_create(name, &ds);
+	if (ret < 0)
+		return ERR_PTR(ret);
+
+	config_group_init_type_name(&ds->ci_group, name, &devshell_type);
+
+	return &ds->ci_group;
+}
+
+static void devshell_release(struct config_group *group,
+			     struct config_item *item)
+{
+	struct vbus_devshell *ds = to_vbus_devshell(item);
+
+	kobject_put(&ds->kobj);
+
+	if (ds->dc)
+		vbus_devclass_put(ds->dc);
+}
+
+static struct configfs_group_operations devices_ops = {
+	.make_group	= devshell_create,
+	.drop_item      = devshell_release,
+};
+
+static struct config_item_type devices_type = {
+	.ct_group_ops	= &devices_ops,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct config_item_type root_type = {
+	.ct_owner	= THIS_MODULE,
+};
+
+int __init vbus_config_init(void)
+{
+	int ret;
+	struct configfs_subsystem *subsys = &vbus_root.ci.subsys;
+
+	config_group_init_type_name(&subsys->su_group, "vbus", &root_type);
+	mutex_init(&subsys->su_mutex);
+
+	subsys->su_group.default_groups = vbus_root.ci.defgroups;
+	subsys->su_group.default_groups[0] = &vbus_root.buses.ci_group;
+	subsys->su_group.default_groups[1] = &vbus_root.devices.ci_group;
+	subsys->su_group.default_groups[2] = NULL;
+
+	config_group_init_type_name(&vbus_root.buses.ci_group,
+				    "instances", &buses_type);
+
+	config_group_init_type_name(&vbus_root.devices.ci_group,
+				    "devices", &devices_type);
+
+	ret = configfs_register_subsystem(subsys);
+	if (ret) {
+		printk(KERN_ERR "Error %d while registering subsystem %s\n",
+		       ret,
+		       subsys->su_group.cg_item.ci_namebuf);
+		goto out_unregister;
+	}
+
+	return 0;
+
+out_unregister:
+	configfs_unregister_subsystem(subsys);
+
+	return ret;
+}
+
+void __exit vbus_config_exit(void)
+{
+	configfs_unregister_subsystem(&vbus_root.ci.subsys);
+}
+
+
diff --git a/kernel/vbus/core.c b/kernel/vbus/core.c
new file mode 100644
index 0000000..033999f
--- /dev/null
+++ b/kernel/vbus/core.c
@@ -0,0 +1,567 @@
+/*
+ * Copyright 2009 Novell.  All Rights Reserved.
+ *
+ * Author:
+ *      Gregory Haskins <ghaskins@...ell.com>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/vbus.h>
+#include <linux/uaccess.h>
+
+#include "vbus.h"
+
+static struct vbus_device_interface *kobj_to_intf(struct kobject *kobj)
+{
+	return container_of(kobj, struct vbus_device_interface, kobj);
+}
+
+static struct vbus_devshell *to_devshell(struct kobject *kobj)
+{
+	return container_of(kobj, struct vbus_devshell, kobj);
+}
+
+static void interface_release(struct kobject *kobj)
+{
+	struct vbus_device_interface *intf = kobj_to_intf(kobj);
+
+	if (intf->ops->release)
+		intf->ops->release(intf);
+}
+
+static struct kobj_type interface_ktype = {
+	.release = interface_release,
+	.sysfs_ops = &kobj_sysfs_ops,
+};
+
+static ssize_t
+type_show(struct kobject *kobj, struct kobj_attribute *attr,
+		  char *buf)
+{
+	struct vbus_device_interface *intf = kobj_to_intf(kobj);
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", intf->type);
+}
+
+static struct kobj_attribute devattr_type =
+	__ATTR_RO(type);
+
+static struct attribute *attrs[] = {
+	&devattr_type.attr,
+	NULL,
+};
+
+static struct attribute_group attr_group = {
+	.attrs = attrs,
+};
+
+/*
+ * Assumes dev->bus->lock is held
+ */
+static void _interface_unregister(struct vbus_device_interface *intf)
+{
+	struct vbus *vbus = intf->vbus;
+	struct vbus_devshell *ds = to_devshell(intf->dev->kobj);
+
+	map_del(&vbus->devices.map, &intf->node);
+	sysfs_remove_link(&ds->intfs, intf->name);
+	sysfs_remove_link(&intf->kobj, "device");
+	sysfs_remove_group(&intf->kobj, &attr_group);
+}
+
+int vbus_device_interface_register(struct vbus_device *dev,
+				   struct vbus *vbus,
+				   struct vbus_device_interface *intf)
+{
+	int ret;
+	struct vbus_devshell *ds = to_devshell(dev->kobj);
+
+	mutex_lock(&vbus->lock);
+
+	if (vbus->next_id == -1) {
+		mutex_unlock(&vbus->lock);
+		return -ENOSPC;
+	}
+
+	intf->id = vbus->next_id++;
+	intf->dev = dev;
+	intf->vbus = vbus;
+
+	ret = map_add(&vbus->devices.map, &intf->node);
+	if (ret < 0) {
+		mutex_unlock(&vbus->lock);
+		return ret;
+	}
+
+	kobject_init_and_add(&intf->kobj, &interface_ktype,
+			     &vbus->devices.kobj, "%ld", intf->id);
+
+	/* Create the basic attribute files associated with this kobject */
+	ret = sysfs_create_group(&intf->kobj, &attr_group);
+	if (ret)
+		goto error;
+
+	/* Create cross-referencing links between the device and bus */
+	ret = sysfs_create_link(&intf->kobj, dev->kobj, "device");
+	if (ret)
+		goto error;
+
+	ret = sysfs_create_link(&ds->intfs, &intf->kobj, intf->name);
+	if (ret)
+		goto error;
+
+	mutex_unlock(&vbus->lock);
+
+	return 0;
+
+error:
+	_interface_unregister(intf);
+	mutex_unlock(&vbus->lock);
+
+	kobject_put(&intf->kobj);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(vbus_device_interface_register);
+
+int vbus_device_interface_unregister(struct vbus_device_interface *intf)
+{
+	struct vbus *vbus = intf->vbus;
+
+	mutex_lock(&vbus->lock);
+	_interface_unregister(intf);
+	mutex_unlock(&vbus->lock);
+
+	kobject_put(&intf->kobj);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(vbus_device_interface_unregister);
+
+static struct vbus_device_interface *node_to_intf(struct rb_node *node)
+{
+	return node ? container_of(node, struct vbus_device_interface, node)
+		: NULL;
+}
+
+static int interface_item_compare(struct rb_node *lhs, struct rb_node *rhs)
+{
+	struct vbus_device_interface *lintf = node_to_intf(lhs);
+	struct vbus_device_interface *rintf = node_to_intf(rhs);
+
+	return lintf->id - rintf->id;
+}
+
+static int interface_key_compare(const void *key, struct rb_node *node)
+{
+	struct vbus_device_interface *intf = node_to_intf(node);
+	unsigned long id = *(unsigned long *)key;
+
+	return id - intf->id;
+}
+
+static struct map_ops interface_map_ops = {
+	.key_compare = &interface_key_compare,
+	.item_compare = &interface_item_compare,
+};
+
+/*
+ *-----------------
+ * member
+ *-----------------
+ */
+
+static struct vbus_member *node_to_member(struct rb_node *node)
+{
+	return node ? container_of(node, struct vbus_member, node) : NULL;
+}
+
+static struct vbus_member *kobj_to_member(struct kobject *kobj)
+{
+	return kobj ? container_of(kobj, struct vbus_member, kobj) : NULL;
+}
+
+static int member_item_compare(struct rb_node *lhs, struct rb_node *rhs)
+{
+	struct vbus_member *lmember = node_to_member(lhs);
+	struct vbus_member *rmember = node_to_member(rhs);
+
+	return lmember->tsk->pid - rmember->tsk->pid;
+}
+
+static int member_key_compare(const void *key, struct rb_node *node)
+{
+	struct vbus_member *member = node_to_member(node);
+	pid_t pid = *(pid_t *)key;
+
+	return pid - member->tsk->pid;
+}
+
+static struct map_ops member_map_ops = {
+	.key_compare = &member_key_compare,
+	.item_compare = &member_item_compare,
+};
+
+static void member_release(struct kobject *kobj)
+{
+	struct vbus_member *member = kobj_to_member(kobj);
+
+	vbus_put(member->vbus);
+	put_task_struct(member->tsk);
+
+	kfree(member);
+}
+
+static struct kobj_type member_ktype = {
+	.release = member_release,
+};
+
+int vbus_associate(struct vbus *vbus, struct task_struct *tsk)
+{
+	struct vbus_member *member;
+	int ret;
+
+	member = kzalloc(sizeof(struct vbus_member), GFP_KERNEL);
+	if (!member)
+		return -ENOMEM;
+
+	mutex_lock(&vbus->lock);
+
+	get_task_struct(tsk);
+	vbus_get(vbus);
+
+	member->vbus = vbus;
+	member->tsk = tsk;
+
+	ret = kobject_init_and_add(&member->kobj, &member_ktype,
+				   &vbus->members.kobj,
+				   "%d", tsk->pid);
+	if (ret < 0)
+		goto error;
+
+	ret = map_add(&vbus->members.map, &member->node);
+	if (ret < 0)
+		goto error;
+
+out:
+	mutex_unlock(&vbus->lock);
+	return 0;
+
+error:
+	kobject_put(&member->kobj);
+	goto out;
+}
+
+int vbus_disassociate(struct vbus *vbus, struct task_struct *tsk)
+{
+	struct vbus_member *member;
+
+	mutex_lock(&vbus->lock);
+
+	member = node_to_member(map_find(&vbus->members.map, &tsk->pid));
+	BUG_ON(!member);
+
+	map_del(&vbus->members.map, &member->node);
+
+	mutex_unlock(&vbus->lock);
+
+	kobject_put(&member->kobj);
+
+	return 0;
+}
+
+/*
+ *-----------------
+ * vbus_subdir
+ *-----------------
+ */
+
+static void vbus_subdir_init(struct vbus_subdir *subdir,
+			     const char *name,
+			     struct kobject *parent,
+			     struct kobj_type *type,
+			     struct map_ops *map_ops)
+{
+	int ret;
+
+	map_init(&subdir->map, map_ops);
+
+	ret = kobject_init_and_add(&subdir->kobj, type, parent, name);
+	BUG_ON(ret < 0);
+}
+
+/*
+ *-----------------
+ * vbus
+ *-----------------
+ */
+
+static void vbus_destroy(struct kobject *kobj)
+{
+	struct vbus *vbus = container_of(kobj, struct vbus, kobj);
+
+	kfree(vbus);
+}
+
+static struct kobj_type vbus_ktype = {
+	.release = vbus_destroy,
+};
+
+static struct kobj_type null_ktype = {
+};
+
+int vbus_create(const char *name, struct vbus **bus)
+{
+	struct vbus *_bus = NULL;
+	int ret;
+
+	_bus = kzalloc(sizeof(struct vbus), GFP_KERNEL);
+	if (!_bus)
+		return -ENOMEM;
+
+	atomic_set(&_bus->refs, 1);
+	mutex_init(&_bus->lock);
+
+	kobject_init_and_add(&_bus->kobj, &vbus_ktype,
+			     vbus_root.buses.kobj, name);
+
+	vbus_subdir_init(&_bus->devices, "devices", &_bus->kobj,
+			 &null_ktype, &interface_map_ops);
+	vbus_subdir_init(&_bus->members, "members", &_bus->kobj,
+			 &null_ktype, &member_map_ops);
+
+	_bus->next_id = 0;
+
+	mutex_lock(&vbus_root.lock);
+
+	ret = map_add(&vbus_root.buses.map, &_bus->node);
+	BUG_ON(ret < 0);
+
+	mutex_unlock(&vbus_root.lock);
+
+	*bus = _bus;
+
+	return 0;
+}
+
+static void devshell_release(struct kobject *kobj)
+{
+	struct vbus_devshell *ds = container_of(kobj,
+						struct vbus_devshell, kobj);
+
+	if (ds->dev) {
+		if (ds->dev->attrs)
+			sysfs_remove_group(&ds->kobj, ds->dev->attrs);
+
+		if (ds->dev->ops->release)
+			ds->dev->ops->release(ds->dev);
+	}
+
+	if (ds->dc)
+		sysfs_remove_link(&ds->kobj, "class");
+
+	kobject_put(&ds->intfs);
+	kfree(ds);
+}
+
+static struct kobj_type devshell_ktype = {
+	.release = devshell_release,
+	.sysfs_ops = &vbus_dev_attr_ops,
+};
+
+static void _interfaces_init(struct vbus_devshell *ds)
+{
+	kobject_init_and_add(&ds->intfs, &null_ktype, &ds->kobj, "interfaces");
+}
+
+int vbus_devshell_create(const char *name, struct vbus_devshell **ds)
+{
+	struct vbus_devshell *_ds = NULL;
+
+	_ds = kzalloc(sizeof(*_ds), GFP_KERNEL);
+	if (!_ds)
+		return -ENOMEM;
+
+	kobject_init_and_add(&_ds->kobj, &devshell_ktype,
+			     vbus_root.devices.kobj, name);
+
+	_interfaces_init(_ds);
+
+	*ds = _ds;
+
+	return 0;
+}
+
+int vbus_devshell_type_set(struct vbus_devshell *ds)
+{
+	int ret;
+
+	if (!ds->dev)
+		return -EINVAL;
+
+	if (!ds->dev->attrs)
+		return 0;
+
+	ret = sysfs_create_link(&ds->kobj, &ds->dc->kobj, "class");
+	if (ret < 0)
+		return ret;
+
+	return sysfs_create_group(&ds->kobj, ds->dev->attrs);
+}
+
+struct vbus *vbus_get(struct vbus *vbus)
+{
+	if (vbus)
+		atomic_inc(&vbus->refs);
+
+	return vbus;
+}
+EXPORT_SYMBOL_GPL(vbus_get);
+
+void vbus_put(struct vbus *vbus)
+{
+	if (vbus && atomic_dec_and_test(&vbus->refs)) {
+		kobject_put(&vbus->devices.kobj);
+		kobject_put(&vbus->members.kobj);
+		kobject_put(&vbus->kobj);
+	}
+}
+EXPORT_SYMBOL_GPL(vbus_put);
+
+long vbus_interface_find(struct vbus *bus,
+			 unsigned long id,
+			 struct vbus_device_interface **intf)
+{
+	struct vbus_device_interface *_intf;
+
+	BUG_ON(!bus);
+
+	mutex_lock(&bus->lock);
+
+	_intf = node_to_intf(map_find(&bus->devices.map, &id));
+	if (likely(_intf))
+		kobject_get(&_intf->kobj);
+
+	mutex_unlock(&bus->lock);
+
+	if (!_intf)
+		return -ENOENT;
+
+	*intf = _intf;
+
+	return 0;
+}
+
+const char *vbus_name(struct vbus *vbus)
+{
+	return vbus ? vbus->kobj.name : NULL;
+}
+
+/*
+ *---------------------
+ * vbus_buses
+ *---------------------
+ */
+
+static struct vbus *node_to_bus(struct rb_node *node)
+{
+	return node ? container_of(node, struct vbus, node) : NULL;
+}
+
+static int bus_item_compare(struct rb_node *lhs, struct rb_node *rhs)
+{
+	struct vbus *lbus = node_to_bus(lhs);
+	struct vbus *rbus = node_to_bus(rhs);
+
+	return strcmp(lbus->kobj.name, rbus->kobj.name);
+}
+
+static int bus_key_compare(const void *key, struct rb_node *node)
+{
+	struct vbus *bus = node_to_bus(node);
+
+	return strcmp(key, bus->kobj.name);
+}
+
+static struct map_ops bus_map_ops = {
+	.key_compare = &bus_key_compare,
+	.item_compare = &bus_item_compare,
+};
+
+struct vbus *vbus_find(const char *name)
+{
+	struct vbus *bus;
+
+	mutex_lock(&vbus_root.lock);
+
+	bus = node_to_bus(map_find(&vbus_root.buses.map, name));
+	if (!bus)
+		goto out;
+
+	vbus_get(bus);
+
+out:
+	mutex_unlock(&vbus_root.lock);
+
+	return bus;
+
+}
+
+struct vbus_root vbus_root;
+
+static ssize_t version_show(struct kobject *kobj,
+			struct kobj_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", VBUS_VERSION);
+}
+
+static struct kobj_attribute version_attr =
+	__ATTR(version, S_IRUGO, version_show, NULL);
+
+static int __init vbus_init(void)
+{
+	int ret;
+
+	mutex_init(&vbus_root.lock);
+
+	ret = vbus_config_init();
+	BUG_ON(ret < 0);
+
+	vbus_root.kobj = kobject_create_and_add("vbus", NULL);
+	BUG_ON(!vbus_root.kobj);
+
+	ret = sysfs_create_file(vbus_root.kobj, &version_attr.attr);
+	BUG_ON(ret);
+
+	ret = vbus_devclass_init();
+	BUG_ON(ret < 0);
+
+	map_init(&vbus_root.buses.map, &bus_map_ops);
+	vbus_root.buses.kobj = kobject_create_and_add("instances",
+						      vbus_root.kobj);
+	BUG_ON(!vbus_root.buses.kobj);
+
+	vbus_root.devices.kobj = kobject_create_and_add("devices",
+							vbus_root.kobj);
+	BUG_ON(!vbus_root.devices.kobj);
+
+	return 0;
+}
+
+late_initcall(vbus_init);
+
+
diff --git a/kernel/vbus/devclass.c b/kernel/vbus/devclass.c
new file mode 100644
index 0000000..3f5ef0d
--- /dev/null
+++ b/kernel/vbus/devclass.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2009 Novell.  All Rights Reserved.
+ *
+ * Author:
+ *      Gregory Haskins <ghaskins@...ell.com>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/vbus.h>
+
+#include "vbus.h"
+
+static struct vbus_devclass *node_to_devclass(struct rb_node *node)
+{
+	return node ? container_of(node, struct vbus_devclass, node) : NULL;
+}
+
+static int devclass_item_compare(struct rb_node *lhs, struct rb_node *rhs)
+{
+	struct vbus_devclass *ldc = node_to_devclass(lhs);
+	struct vbus_devclass *rdc = node_to_devclass(rhs);
+
+	return strcmp(ldc->name, rdc->name);
+}
+
+static int devclass_key_compare(const void *key, struct rb_node *node)
+{
+	struct vbus_devclass *dc = node_to_devclass(node);
+
+	return strcmp((const char *)key, dc->name);
+}
+
+static struct map_ops devclass_map_ops = {
+	.key_compare = &devclass_key_compare,
+	.item_compare = &devclass_item_compare,
+};
+
+int __init vbus_devclass_init(void)
+{
+	struct vbus_devclasses *c = &vbus_root.devclasses;
+
+	map_init(&c->map, &devclass_map_ops);
+
+	c->kobj = kobject_create_and_add("deviceclass", vbus_root.kobj);
+	BUG_ON(!c->kobj);
+
+	return 0;
+}
+
+static void devclass_release(struct kobject *kobj)
+{
+	struct vbus_devclass *dc = container_of(kobj,
+						struct vbus_devclass,
+						kobj);
+
+	if (dc->ops->release)
+		dc->ops->release(dc);
+}
+
+static struct kobj_type devclass_ktype = {
+	.release = devclass_release,
+};
+
+int vbus_devclass_register(struct vbus_devclass *dc)
+{
+	int ret;
+
+	mutex_lock(&vbus_root.lock);
+
+	ret = map_add(&vbus_root.devclasses.map, &dc->node);
+	if (ret < 0)
+		goto out;
+
+	ret = kobject_init_and_add(&dc->kobj, &devclass_ktype,
+				   vbus_root.devclasses.kobj, dc->name);
+	if (ret < 0) {
+		map_del(&vbus_root.devclasses.map, &dc->node);
+		goto out;
+	}
+
+out:
+	mutex_unlock(&vbus_root.lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(vbus_devclass_register);
+
+int vbus_devclass_unregister(struct vbus_devclass *dc)
+{
+	mutex_lock(&vbus_root.lock);
+	map_del(&vbus_root.devclasses.map, &dc->node);
+	mutex_unlock(&vbus_root.lock);
+
+	kobject_put(&dc->kobj);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(vbus_devclass_unregister);
+
+struct vbus_devclass *vbus_devclass_find(const char *name)
+{
+	struct vbus_devclass *dev;
+
+	mutex_lock(&vbus_root.lock);
+	dev = node_to_devclass(map_find(&vbus_root.devclasses.map, name));
+	if (dev)
+		dev = vbus_devclass_get(dev);
+	mutex_unlock(&vbus_root.lock);
+
+	return dev;
+}
diff --git a/kernel/vbus/map.c b/kernel/vbus/map.c
new file mode 100644
index 0000000..a3bd841
--- /dev/null
+++ b/kernel/vbus/map.c
@@ -0,0 +1,72 @@
+
+#include <linux/errno.h>
+
+#include "map.h"
+
+void map_init(struct map *map, struct map_ops *ops)
+{
+	map->root = RB_ROOT;
+	map->ops = ops;
+}
+
+int map_add(struct map *map, struct rb_node *node)
+{
+	int		ret = 0;
+	struct rb_root *root;
+	struct rb_node **new, *parent = NULL;
+
+	root = &map->root;
+	new  = &(root->rb_node);
+
+	/* Figure out where to put new node */
+	while (*new) {
+		int val;
+
+		parent = *new;
+
+		val = map->ops->item_compare(node, *new);
+		if (val < 0)
+			new = &((*new)->rb_left);
+		else if (val > 0)
+			new = &((*new)->rb_right);
+		else {
+			ret = -EEXIST;
+			break;
+		}
+	}
+
+	if (!ret) {
+		/* Add new node and rebalance tree. */
+		rb_link_node(node, parent, new);
+		rb_insert_color(node, root);
+	}
+
+	return ret;
+}
+
+struct rb_node *map_find(struct map *map, const void *key)
+{
+	struct rb_node *node;
+
+	node = map->root.rb_node;
+
+	while (node) {
+		int val;
+
+		val = map->ops->key_compare(key, node);
+		if (val < 0)
+			node = node->rb_left;
+		else if (val > 0)
+			node = node->rb_right;
+		else
+			break;
+	}
+
+	return node;
+}
+
+void map_del(struct map *map, struct rb_node *node)
+{
+	rb_erase(node, &map->root);
+}
+
diff --git a/kernel/vbus/map.h b/kernel/vbus/map.h
new file mode 100644
index 0000000..7fb5164
--- /dev/null
+++ b/kernel/vbus/map.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2009 Novell.  All Rights Reserved.
+ *
+ * Author:
+ *      Gregory Haskins <ghaskins@...ell.com>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __VBUS_MAP_H__
+#define __VBUS_MAP_H__
+
+#include <linux/rbtree.h>
+
+struct map_ops {
+	int (*item_compare)(struct rb_node *lhs, struct rb_node *rhs);
+	int (*key_compare)(const void *key, struct rb_node *item);
+};
+
+struct map {
+	struct rb_root root;
+	struct map_ops *ops;
+};
+
+void map_init(struct map *map, struct map_ops *ops);
+int map_add(struct map *map, struct rb_node *node);
+struct rb_node *map_find(struct map *map, const void *key);
+void map_del(struct map *map, struct rb_node *node);
+
+#endif /* __VBUS_MAP_H__ */
diff --git a/kernel/vbus/vbus.h b/kernel/vbus/vbus.h
new file mode 100644
index 0000000..1266d69
--- /dev/null
+++ b/kernel/vbus/vbus.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2009 Novell.  All Rights Reserved.
+ *
+ * Author:
+ *      Gregory Haskins <ghaskins@...ell.com>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __VBUS_H__
+#define __VBUS_H__
+
+#include <linux/configfs.h>
+#include <linux/rbtree.h>
+#include <linux/mutex.h>
+#include <linux/kobject.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+
+#include "map.h"
+
+#define VBUS_VERSION 1
+
+struct vbus_subdir {
+	struct map     map;
+	struct kobject kobj;
+};
+
+struct vbus {
+	struct {
+		struct config_group group;
+		struct config_group perms;
+		struct config_group *defgroups[2];
+	} ci;
+
+	atomic_t refs;
+	struct mutex lock;
+	struct kobject kobj;
+	struct vbus_subdir devices;
+	struct vbus_subdir members;
+	unsigned long next_id;
+	struct rb_node node;
+};
+
+struct vbus_member {
+	struct rb_node      node;
+	struct task_struct *tsk;
+	struct vbus        *vbus;
+	struct kobject      kobj;
+};
+
+struct vbus_devclasses {
+	struct kobject *kobj;
+	struct map map;
+};
+
+struct vbus_buses {
+	struct config_group ci_group;
+	struct map map;
+	struct kobject *kobj;
+};
+
+struct vbus_devshell {
+	struct config_group ci_group;
+	struct vbus_device *dev;
+	struct vbus_devclass *dc;
+	struct kobject kobj;
+	struct kobject intfs;
+};
+
+struct vbus_devices {
+	struct config_group ci_group;
+	struct kobject *kobj;
+};
+
+struct vbus_root {
+	struct {
+		struct configfs_subsystem subsys;
+		struct config_group      *defgroups[3];
+	} ci;
+
+	struct mutex            lock;
+	struct kobject         *kobj;
+	struct vbus_devclasses  devclasses;
+	struct vbus_buses       buses;
+	struct vbus_devices     devices;
+};
+
+extern struct vbus_root vbus_root;
+extern struct sysfs_ops vbus_dev_attr_ops;
+
+int vbus_config_init(void);
+int vbus_devclass_init(void);
+
+int vbus_create(const char *name, struct vbus **bus);
+
+int vbus_devshell_create(const char *name, struct vbus_devshell **ds);
+struct vbus_devclass *vbus_devclass_find(const char *name);
+int vbus_devshell_type_set(struct vbus_devshell *ds);
+
+long vbus_interface_find(struct vbus *vbus,
+			 unsigned long id,
+			 struct vbus_device_interface **intf);
+
+#endif /* __VBUS_H__ */

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ