1
Fork 0

new post: Xen - split driver, initial communication

This commit is contained in:
Jan-Erik Rediger 2016-12-20 16:01:19 +01:00
parent ef9b45c981
commit 8d12e80d81

View file

@ -0,0 +1,211 @@
---
layout: post
title: "Xen - split driver, initial communication"
date: 20.12.2016 16:00
---
In the [previous post](/2016/12/02/xen-a-backend-frontend-driver-example/) I explained how to initially setup a split driver
for Xen with the backend in *dom0* and the frontend in a *domU*.
This time we are taking a look at the internal states each side goes through.
Most of this code is a trimmed down version of the [Xen network driver](https://github.com/torvalds/linux/blob/bc3913a5378cd0ddefd1dfec6917cc12eb23a946/drivers/net/xen-netfront.c#L2024-L2060).
The full code can be found in [chapter 2](https://github.com/badboy/xen-split-driver-example/tree/master/chapter02) of the example repository.
## Background
The frontend part of the driver sits in an uprivileged *domU* and gets its input from the kernel in this virtual machine.
Depending on its usecase it then passed on commands what to do and the data over to the backend part,
sitting in an privileged domain such as *dom0*.
For example in case of the network driver, the *domU* kernel generates network packets
which are passed over to the backend,
which is then responsible to transfering this data to the actual network card.
Before all of this can happen both parts need to be able to communicate with each other.
Each part must probably set up a few things before it can do its job.
Some of these things must be advanced in lock-step, so each part advances to its next status
and then waits for counterpart to advance as well.
## The state machine
Internally this is all done through a state machine.
Both sides start in the `XenbusStateInitialising` state.
The goal is to reach `XenbusStateConnected` once fully setup.
If no setup is required at all, it is as easy as saying so:
~~~c
xenbus_switch_state(dev, XenbusStateConnected);
~~~
Of course a driver rarely has to do nothing at all.
Instead in each intermediate state some work can be done.
This results in a fairly large state machine on both ends, but most of it is just boilerplate.
This results in about 30 lines extra in the frontend and 100 lines in the backend.
In this blog post I will focus only on a few relevant lines.
Both sides gain another callback function, to be notified when the other side changes its state.
This way they can advance in lock-step.
~~~c
static void mydevicefront_otherend_changed(struct xenbus_device *dev,
enum xenbus_state backend_state)
}
static struct xenbus_driver mydevicefront_driver = {
.ids = mydevicefront_ids,
.probe = mydevicefront_probe,
.otherend_changed = mydevicefront_otherend_changed,
};
~~~
(The backend as a similar one)
The passed state will tell us the new state of the other side.
In the frontend we can simply wait for different state switches and switch over the frontend as well,
eventually reaching `XenbusStateConnected`
~~~c
switch (backend_state)
{
case XenbusStateInitWait:
if (dev->state != XenbusStateInitialising)
break;
if (frontend_connect(dev) != 0)
break;
xenbus_switch_state(dev, XenbusStateConnected);
break;
}
~~~
The `frontend_connect` function should then set up everything necessary.
The backend has a similar function:
~~~c
static void mydeviceback_otherend_changed(struct xenbus_device *dev, enum xenbus_state frontend_state)
{
switch (frontend_state) {
case XenbusStateConnected:
set_backend_state(dev, XenbusStateConnected);
break;
}
~~~
This defers to yet another function, actually just boilerplate to ensure the right order of state changes:
~~~c
static void set_backend_state(struct xenbus_device *dev,
enum xenbus_state state)
{
while (dev->state != state) {
switch (dev->state) {
case XenbusStateInitWait:
switch (state) {
case XenbusStateConnected:
backend_connect(dev);
xenbus_switch_state(dev, XenbusStateConnected);
break;
case XenbusStateClosing:
case XenbusStateClosed:
xenbus_switch_state(dev, XenbusStateClosing);
break;
default:
BUG();
}
break;
}
}
}
~~~
*Note: The whole state machine switching was taken from the network driver and may be reduced for other cases.*
Again, this calls into another function `backend_connect` where we can handle the setup.
For every invalid state switch it will trigger the `BUG()` macro, which crashes the module and in turn the kernel,
but at least you know where to start.
Last but not least let's set the initial state in the backend:
~~~c
static int mydeviceback_probe(struct xenbus_device *dev,
const struct xenbus_device_id *id)
{
xenbus_switch_state(dev, XenbusStateInitialising);
return 0;
}
~~~
With the module code done, we need one last change: The guest domain must be able to write its state back to the XenStore.
Thus we set the correct permissions on paths in the XenStore using:
~~~
xenstore-chmod $DOM0_KEY r0 b$DOMU_ID
xenstore-chmod $DOMU_KEY r$DOMU_ID b0
~~~
The XenStore permissions are a bit unusual.
There are 4 different modes per file: **r**ead, **w**rite or **b**oth, **n**o access.
However, the owner of a file always has full access (**b**oth).
The very first permission always sets the owner and the permissions for any remaining user.
Every additional permission overwrites this first specified permission for the given user.
So the above `r0 b$DOMU_ID` means:
* Owner is domain 0, with full rights
* Every not-further specified domain can read the key
* The guest domain has read and write access (**b**oth)
For the second line it is the other way around.
## Run it
With everything in the code, we can compile the modules and load them into the kernel.
~~~
dom0# insmod mydeviceback.ko
domU# insmod mydevicefront.ko
d0m0# ./activate.sh 1
~~~
In the kernel log output of `dom0` you should see something along the lines:
~~~
Hello World!
Probe called. We are good to go.
Connecting the backend now.
~~~
And in the log output of `domU` you should see:
~~~
Hello World!
Probe called. We are good to go.
Connecting the frontend now.
Other side says it is connected as well.
~~~
At this point both sides are in `XenbusStateConnected` mode and can communicate.
As always, the full code can be found in
[chapter 2](https://github.com/badboy/xen-split-driver-example/tree/master/chapter02) of the example repository.
## Up next
Now that we know each state to go through,
we can set up communication through event channels.
We take a closer look at this in the next post.