[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20090421183427.12548.88379.stgit@dev.haskins.net>
Date: Tue, 21 Apr 2009 14:34:28 -0400
From: Gregory Haskins <ghaskins@...ell.com>
To: linux-kernel@...r.kernel.org
Cc: kvm@...r.kernel.org, agraf@...e.de, pmullaney@...ell.com,
pmorreale@...ell.com, alext@...ell.com, anthony@...emonkey.ws,
rusty@...tcorp.com.au, netdev@...r.kernel.org, avi@...hat.com,
bhutchings@...arflare.com, andi@...stfloor.org, gregkh@...e.de,
chrisw@...s-sol.org, shemminger@...tta.com, alex.williamson@...com
Subject: [RFC PATCH v3 02/17] vbus: add virtual-bus definitions
See Documentation/vbus.txt for details
Signed-off-by: Gregory Haskins <ghaskins@...ell.com>
---
Documentation/vbus.txt | 385 +++++++++++++++++++++++++++++
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, 2318 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..7e96758
--- /dev/null
+++ b/Documentation/vbus.txt
@@ -0,0 +1,385 @@
+
+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.
+They are represented as root-level objects under /config/vbus/devices as
+opposed to bus subordinate objects 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 bc25b9f..e222395 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1978,6 +1978,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 f715597..25a1c60 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -81,6 +81,7 @@
#include <linux/elf.h>
#include <linux/pid_namespace.h>
#include <linux/fs_struct.h>
+#include <linux/vbus.h>
#include "internal.h"
/* NOTE:
@@ -1048,6 +1049,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,
@@ -2539,6 +2632,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 b4c38bc..5e3ff74 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -98,6 +98,7 @@ struct robust_list_head;
struct bio;
struct bts_tracer;
struct fs_struct;
+struct vbus;
/*
* List of flags we want to share for kernel threads,
@@ -1339,6 +1340,9 @@ struct task_struct {
struct held_lock held_locks[MAX_LOCK_DEPTH];
gfp_t lockdep_reclaim_gfp;
#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 4242366..cc06c63 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -95,6 +95,7 @@ obj-$(CONFIG_FUNCTION_TRACER) += trace/
obj-$(CONFIG_TRACING) += trace/
obj-$(CONFIG_SMP) += sched_cpupri.o
obj-$(CONFIG_SLOW_WORK) += slow-work.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 abf9cf3..c86875d 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -49,6 +49,7 @@
#include <linux/fs_struct.h>
#include <linux/init_task.h>
#include <trace/sched.h>
+#include <linux/vbus.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
@@ -966,6 +967,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 b9e2edd..5954d6c 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -63,6 +63,7 @@
#include <linux/fs_struct.h>
#include <trace/sched.h>
#include <linux/magic.h>
+#include <linux/vbus.h>
#include <asm/pgtable.h>
#include <asm/pgalloc.h>
@@ -1266,6 +1267,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