Skip to contentSkip to conversation

xen: add xenfs to allow usermode <-> Xen interaction

PATCH In review Jeremy Fitzhardinge 3 participants 9 messages Dec 16, 2008 – Dec 18, 2008 Xen hypervisor interface

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 xen: add xenfs to allow usermode <-> Xen interaction

[ Reviewers: This is in drivers/xen to keep it close to the code it is and will be using. Would people
prefer to see it in fs/xenfs? -J ]

From: Alex Zeffertt <[email protected]>

The xenfs filesystem exports various interfaces to usermode. Initially this exports a file to allow usermode to interact with xenbus/xenstore.

Traditionally this appeared in /proc/xen. Rather than extending procfs, this patch adds a backward-compat mountpoint on /proc/xen, and provides a xenfs filesystem which can be mounted there.

---

drivers/xen/Kconfig               |   24 +
drivers/xen/Makefile              |    2 drivers/xen/xenbus/xenbus_probe.c |   16 +
drivers/xen/xenbus/xenbus_xs.c    |    1 drivers/xen/xenfs/Makefile        |    3 drivers/xen/xenfs/super.c         |   65 +++++
drivers/xen/xenfs/xenbus.c        |  465 +++++++++++++++++++++++++++++++++++++
drivers/xen/xenfs/xenfs.h         |    6 include/xen/xenbus.h              |    2 9 files changed, 582 insertions(+), 2 deletions(-)

===================================================================

drivers/xen/Kconfig +0 -0
@@ -17,3 +17,27 @@

is not accidentally visible to other domains. Is it more secure, but slightly less efficient.
If in doubt, say yes.
+
+config XENFS
+ tristate "Xen filesystem"
+ depends on XEN
+ default y
+ help

+	  The xen filesystem provides a way for domains to share
+	  information with each other and with the hypervisor.
+	  For example, by reading and writing the "xenbus" file, guests
+	  may pass arbitrary information to the initial domain.
+	  If in doubt, say yes.
+
+config XEN_COMPAT_XENFS
+       bool "Create compatibility mount point /proc/xen"
+       depends on XENFS
+       default y
+       help
+         The old xenstore userspace tools expect to find "xenbus"
+         under /proc/xen, but "xenbus" is now found at the root of the
+	  xenfs filesystem.  Selecting this causes the kernel to create
+         the compatibilty mount point /proc/xen if it is running on
+         a xen platform.
+         If in doubt, say yes.

+
===================================================================

drivers/xen/Makefile +0 -0
@@ -1,5 +1,7 @@

obj-y += grant-table.o features.o events.o manage.o
obj-y += xenbus/
+
obj-$(CONFIG_HOTPLUG_CPU) += cpu_hotplug.o
obj-$(CONFIG_XEN_XENCOMM) += xencomm.o
obj-$(CONFIG_XEN_BALLOON) += balloon.o
+obj-$(CONFIG_XENFS) += xenfs/
\ No newline at end of file
===================================================================

drivers/xen/xenbus/xenbus_probe.c +0 -0
@@ -40,6 +40,7 @@

#include <linux/ctype.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
+#include <linux/proc_fs.h>
#include <linux/notifier.h>
#include <linux/kthread.h>
#include <linux/mutex.h>
@@ -55,7 +56,10 @@
#include "xenbus_comms.h"
#include "xenbus_probe.h"

+
int xen_store_evtchn; +EXPORT_SYMBOL(xen_store_evtchn);
+
struct xenstore_domain_interface *xen_store_interface; static unsigned long xen_store_mfn;

@@ -166,6 +170,9 @@

	return read_otherend_details(xendev, "backend-id", "backend");
}

+static struct device_attribute xenbus_dev_attrs[] = {
+ __ATTR_NULL
+};

/* Bus type for frontend drivers. */
static struct xen_bus_type xenbus_frontend = {
@@ -180,6 +187,7 @@
.probe = xenbus_dev_probe,
.remove = xenbus_dev_remove,
.shutdown = xenbus_dev_shutdown,
+ .dev_attrs= xenbus_dev_attrs,

	},
};

@@ -874,6 +882,14 @@

	if (!xen_initial_domain())
		xenbus_probe(NULL);

+#ifdef CONFIG_XEN_COMPAT_XENFS
+ /*
+ * Create xenfs mountpoint in /proc for compatibility with
+ * utilities that expect to find "xenbus" under "/proc/xen".
+ */
+ proc_mkdir("xen", NULL);
+#endif
+

	return 0;

out_unreg_back:
===================================================================

drivers/xen/xenbus/xenbus_xs.c +0 -0
@@ -184,6 +184,7 @@
 
	return ret;
}

+EXPORT_SYMBOL(xenbus_dev_request_and_reply);

/* Send message to xs, get kmalloc'ed reply. ERR_PTR() on error. */ static void *xs_talkv(struct xenbus_transaction t,
===================================================================

drivers/xen/xenfs/Makefile new file +3 -0
@@ -0,0 +1,3 @@
+obj-$(CONFIG_XENFS) += xenfs.o
+
+xenfs-objs = super.o xenbus.o
\ No newline at end of file

===================================================================

drivers/xen/xenfs/super.c new file +65 -0

65 lines not on this page · raw message

===================================================================

drivers/xen/xenfs/xenbus.c new file +465 -0

465 lines not on this page · raw message

===================================================================

drivers/xen/xenfs/xenfs.h new file +6 -0
@@ -0,0 +1,6 @@
+#ifndef _XENFS_XENBUS_H
+#define _XENFS_XENBUS_H
+
+extern const struct file_operations xenbus_file_ops;
+
+#endif	/* _XENFS_XENBUS_H */

===================================================================

include/xen/xenbus.h +0 -0
@@ -136,8 +136,6 @@

/* Nil transaction ID. */
#define XBT_NIL ((struct xenbus_transaction) { 0 })

-int __init xenbus_dev_init(void);
-
char **xenbus_directory(struct xenbus_transaction t,

			const char *dir, const char *node, unsigned int *num);

void *xenbus_read(struct xenbus_transaction t,

#1 lore raw

Review

Quoted 5 lines · Jeremy Fitzhardinge

[ Reviewers: This is in drivers/xen to keep it close to the code it is
and will be using. Would people prefer to see it in fs/xenfs? -J ]

(quoted diff) +2 -0
+config XENFS
+	tristate "Xen filesystem"

there's about two dozen filesystems that live not in fs/*:

./net/socket.c: register_filesystem(&sock_fs_type);
./net/sunrpc/rpc_pipe.c: err = register_filesystem(&rpc_pipe_fs_type);
./security/selinux/selinuxfs.c: err = register_filesystem(&sel_fs_type);
./security/inode.c: retval = register_filesystem(&fs_type);
./security/smack/smackfs.c: err = register_filesystem(&smk_fs_type);
./drivers/usb/core/inode.c: retval = register_filesystem(&usb_fs_type);
./drivers/usb/gadget/inode.c: status = register_filesystem (&gadgetfs_type);
./drivers/oprofile/oprofilefs.c: return register_filesystem(&oprofilefs_type);
./drivers/misc/ibmasm/ibmasmfs.c: return register_filesystem(&ibmasmfs_type);
./drivers/isdn/capi/capifs.c: err = register_filesystem(&capifs_fs_type);
./drivers/infiniband/hw/ipath/ipath_fs.c: return register_filesystem(&ipathfs_fs_type);
./drivers/infiniband/core/uverbs_main.c: ret = register_filesystem(&uverbs_event_fs);
./mm/shmem.c: error = register_filesystem(&tmpfs_fs_type);
./mm/tiny-shmem.c: BUG_ON(register_filesystem(&tmpfs_fs_type) != 0);
./ipc/mqueue.c: error = register_filesystem(&mqueue_fs_type);
./kernel/cpuset.c: err = register_filesystem(&cpuset_fs_type);
./kernel/cgroup.c: err = register_filesystem(&cgroup_fs_type);
./include/linux/fs.h:extern int register_filesystem(struct file_system_type *);
./arch/s390/hypfs/inode.c: rc = register_filesystem(&hypfs_type);
./arch/ia64/kernel/perfmon.c: int err = register_filesystem(&pfm_fs_type);
./arch/powerpc/platforms/cell/spufs/inode.c: ret = register_filesystem(&spufs_type);

by the looks of it it wants to live in drivers/xen/. It's a minimalistic API-only filesystem.

Nevertheless it would be nice to have Acks from FS experts. Security, races, obsoleteness of approach, etc.

Ingo

#2 lore raw
In reply to Jeremy Fitzhardinge
Quoted 2 lines · On Tue, 16 Dec 2008 12:27:37 -0800 Jeremy Fitzhardinge <[email protected]> wrote:

[ Reviewers: This is in drivers/xen to keep it close to the code it is and will be using. Would people
prefer to see it in fs/xenfs? -J ]

Well it would be nice to keep filesystems under ./fs, but `grep -rl register_filesystem .' says we screwed that pooch.

Quoted 24 lines

From: Alex Zeffertt <[email protected]>

The xenfs filesystem exports various interfaces to usermode. Initially this exports a file to allow usermode to interact with xenbus/xenstore.

Traditionally this appeared in /proc/xen. Rather than extending procfs, this patch adds a backward-compat mountpoint on /proc/xen, and provides a xenfs filesystem which can be mounted there.

...

drivers/xen/xenfs/super.c new file +9 -0
@@ -0,0 +1,65 @@
+/*
+ *  xenfs.c - a filesystem for passing info between the a domain and
+ *  the hypervisor.
+ *
+ * 2008-10-07  Alex Zeffertt    Replaced /proc/xen/xenbus with xenfs filesystem
+ *                              and /proc/xen compatibility mount point.
+ *                              Turned xenfs into a loadable module.
+ */
+

drivers/xen/xenfs/super.c line 19

Quoted 10 lines
drivers/xen/xenfs/super.c new file +10 -0
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+
+#include "xenfs.h"
+
+#include <asm/xen/hypervisor.h>
+
+#define XENFS_MAGIC	0xabba1974

Move to include/linux/magic.h, please.

drivers/xen/xenfs/super.c line 22

Quoted 2 lines
drivers/xen/xenfs/super.c new file +2 -0
+MODULE_DESCRIPTION("Xen filesystem");
+MODULE_LICENSE("GPL");

MODULE_AUTHOR()?

./MAINTAINERS?

Quoted 35 lines
drivers/xen/xenfs/super.c new file +31 -0
+static int xenfs_fill_super(struct super_block *sb, void *data, int silent)
+{
+	static struct tree_descr xenfs_files[] = {
+		[2] = {"xenbus", &xenbus_file_ops, S_IRUSR|S_IWUSR},
+		{""},
+	};
+
+	return simple_fill_super(sb, XENFS_MAGIC, xenfs_files);
+}
+
 
...
 
+struct xenbus_transaction_holder {
+	struct list_head list;
+	struct xenbus_transaction handle;
+};
+
+struct read_buffer {
+	struct list_head list;
+	unsigned int cons;
+	unsigned int len;
+	char msg[];
+};
 
+struct xenbus_file_priv {
+	/* In-progress transaction. */
+	struct list_head transactions;
+
+	/* Active watches. */
+	struct list_head watches;
+
+	/* Partial request. */
+	unsigned int len;
+	union {

drivers/xen/xenfs/super.c

Quoted 10 lines
drivers/xen/xenfs/super.c new file +10 -0
+		struct xsd_sockmsg msg;
+		char buffer[PAGE_SIZE];
+	} u;
+
+	/* Response queue. */
+	struct list_head read_buffers;
+	wait_queue_head_t read_waitq;
+
+	struct mutex reply_mutex;
+};

It would be useful to document the locking for the list_head's (at least).

Quoted 12 lines
drivers/xen/xenfs/super.c new file +12 -0
+
+static ssize_t xenbus_file_read(struct file *filp,
+			       char __user *ubuf,
+			       size_t len, loff_t *ppos)
+{
+	struct xenbus_file_priv *u = filp->private_data;
+	struct read_buffer *rb;
+	int i, ret;
+
+	mutex_lock(&u->reply_mutex);
+	while (list_empty(&u->read_buffers)) {
+		mutex_unlock(&u->reply_mutex);

drivers/xen/xenfs/super.c

Quoted 10 lines
drivers/xen/xenfs/super.c new file +10 -0
+		ret = wait_event_interruptible(u->read_waitq,
+					       !list_empty(&u->read_buffers));
+		if (ret)
+			return ret;
+		mutex_lock(&u->reply_mutex);
+	}
+
+	rb = list_entry(u->read_buffers.next, struct read_buffer, list);
+	for (i = 0; i < len;) {
+		ret = put_user(rb->msg[rb->cons], ubuf + i);

So mmap_sem nests inside ->reply_mutex. Has this all been carefully tested with lockdep?

Quoted 5 lines
drivers/xen/xenfs/super.c new file +5 -0
+		if (ret) {
+			mutex_unlock(&u->reply_mutex);
+			return ret;
+		}
+		i++;

drivers/xen/xenfs/super.c

Quoted 10 lines
drivers/xen/xenfs/super.c new file +10 -0
+		rb->cons++;
+		if (rb->cons == rb->len) {
+			list_del(&rb->list);
+			kfree(rb);
+			if (list_empty(&u->read_buffers))
+				break;
+			rb = list_entry(u->read_buffers.next,
+					struct read_buffer, list);
+		}
+	}

This code will misbehave if it ever receives a read_buffer with len==0.

drivers/xen/xenfs/super.c

Quoted 11 lines
drivers/xen/xenfs/super.c new file +11 -0
+	mutex_unlock(&u->reply_mutex);
+
+	return i;
+}
+
+static int queue_reply(struct list_head *list, char *data, unsigned int len)
+{
+	struct read_buffer *rb;
+
+	if (len == 0)
+		return 0;

That should fix it.

drivers/xen/xenfs/super.c

Quoted 10 lines
drivers/xen/xenfs/super.c new file +10 -0
+	rb = kmalloc(sizeof(*rb) + len, GFP_KERNEL);
+	if (rb == NULL)
+		return -ENOMEM;
+
+	rb->cons = 0;
+	rb->len = len;
+
+	memcpy(rb->msg, data, len);
+
+	list_add_tail(&rb->list, list);

So the caller of this function must hold ->reply_mutex.

You thought that was secret but I found it out!

Quoted 9 lines
drivers/xen/xenfs/super.c new file +9 -0
+	return 0;
+}
+
+/* Free all the read_buffer s on a list.
+ * Caller must have sole reference to list.
+ */
+static void queue_cleanup(struct list_head  *list)
+{
+	struct read_buffer *rb;

drivers/xen/xenfs/super.c

Quoted 10 lines
drivers/xen/xenfs/super.c new file +10 -0
+
+	while (!list_empty(list)) {
+		rb = list_entry(list->next, struct read_buffer, list);
+		list_del(list->next);
+		kfree(rb);
+	}
+}
+
+struct watch_adapter {
+	struct list_head list;

locking is?

Quoted 17 lines
drivers/xen/xenfs/super.c new file +14 -0
+	struct xenbus_watch watch;
+	struct xenbus_file_priv *dev_data;
+	char *token;
+};
+
 
...
 
+static LIST_HEAD(watch_list);
+
+static ssize_t xenbus_file_write(struct file *filp,
+				const char __user *ubuf,
+				size_t len, loff_t *ppos)
+{
+	struct xenbus_file_priv *u = filp->private_data;
+	struct xenbus_transaction_holder *trans = NULL;
+	uint32_t msg_type;

drivers/xen/xenfs/super.c

Quoted 10 lines
drivers/xen/xenfs/super.c new file +10 -0
+	void *reply;
+	char *path, *token;
+	int err, rc = len;
+	int ret;
+	LIST_HEAD(staging_q);
+
+	if ((len + u->len) > sizeof(u->u.buffer)) {
+		rc = -EINVAL;
+		goto out;
+	}

Now what's this doing?

One would expect a large write to get chunked up into smaller writes by the kernel.

This code is poorly documented.

Quoted 144 lines
drivers/xen/xenfs/super.c new file +144 -0
+	if (copy_from_user(u->u.buffer + u->len, ubuf, len) != 0) {
+		rc = -EFAULT;
+		goto out;
+	}
+
+	u->len += len;
+	if ((u->len < sizeof(u->u.msg)) ||
+	    (u->len < (sizeof(u->u.msg) + u->u.msg.len)))
+		return rc;
+
+	msg_type = u->u.msg.type;
+
+	switch (msg_type) {
+	case XS_TRANSACTION_START:
+	case XS_TRANSACTION_END:
+	case XS_DIRECTORY:
+	case XS_READ:
+	case XS_GET_PERMS:
+	case XS_RELEASE:
+	case XS_GET_DOMAIN_PATH:
+	case XS_WRITE:
+	case XS_MKDIR:
+	case XS_RM:
+	case XS_SET_PERMS:
+		if (msg_type == XS_TRANSACTION_START) {
+			trans = kmalloc(sizeof(*trans), GFP_KERNEL);
+			if (!trans) {
+				rc = -ENOMEM;
+				goto out;
+			}
+		}
+
+		reply = xenbus_dev_request_and_reply(&u->u.msg);
+		if (IS_ERR(reply)) {
+			kfree(trans);
+			rc = PTR_ERR(reply);
+			goto out;
+		}
+
+		if (msg_type == XS_TRANSACTION_START) {
+			trans->handle.id = simple_strtoul(reply, NULL, 0);
+			list_add(&trans->list, &u->transactions);
+		} else if (msg_type == XS_TRANSACTION_END) {
+			list_for_each_entry(trans, &u->transactions, list)
+				if (trans->handle.id == u->u.msg.tx_id)
+					break;
+			BUG_ON(&trans->list == &u->transactions);
+			list_del(&trans->list);
+			kfree(trans);
+		}
+		mutex_lock(&u->reply_mutex);
+		ret = queue_reply(&staging_q,
+				  (char *)&u->u.msg, sizeof(u->u.msg));
+		if (!ret)
+			ret = queue_reply(&staging_q,
+					  (char *)reply, u->u.msg.len);
+		if (!ret) {
+			list_splice_tail(&staging_q, &u->read_buffers);
+			wake_up(&u->read_waitq);
+		} else {
+			queue_cleanup(&staging_q);
+			rc = ret;
+		}
+		mutex_unlock(&u->reply_mutex);
+		kfree(reply);
+		break;
+
+	case XS_WATCH:
+	case XS_UNWATCH: {
+		struct watch_adapter *watch, *tmp_watch;
+		static const char XS_RESP[] = "OK";
+		struct xsd_sockmsg hdr;
+
+		path = u->u.buffer + sizeof(u->u.msg);
+		token = memchr(path, 0, u->u.msg.len);
+		if (token == NULL) {
+			rc = -EILSEQ;
+			goto out;
+		}
+		token++;
+
+		if (msg_type == XS_WATCH) {
+			watch = kzalloc(sizeof(*watch), GFP_KERNEL);
+			if (watch == NULL) {
+				rc = -ENOMEM;
+				goto out;
+			}
+			watch->watch.node =
+				kmalloc(strlen(path)+1, GFP_KERNEL);
+			if (watch->watch.node == NULL) {
+				kfree(watch);
+				rc = -ENOMEM;
+				goto out;
+			}
+			strcpy((char *)watch->watch.node, path);
+			watch->watch.callback = watch_fired;
+			watch->token =
+				kmalloc(strlen(token)+1, GFP_KERNEL);
+			if (watch->token == NULL) {
+				kfree(watch->watch.node);
+				kfree(watch);
+				rc = -ENOMEM;
+				goto out;
+			}
+			strcpy(watch->token, token);
+			watch->dev_data = u;
+
+			err = register_xenbus_watch(&watch->watch);
+			if (err) {
+				free_watch_adapter(watch);
+				rc = err;
+				goto out;
+			}
+
+			list_add(&watch->list, &u->watches);
+		} else {
+			list_for_each_entry_safe(watch, tmp_watch,
+						 &u->watches, list) {
+				if (!strcmp(watch->token, token) &&
+				    !strcmp(watch->watch.node, path)) {
+					unregister_xenbus_watch(&watch->watch);
+					list_del(&watch->list);
+					free_watch_adapter(watch);
+					break;
+				}
+			}
+		}
+
+		hdr.type = msg_type;
+		hdr.len = sizeof(XS_RESP);
+		mutex_lock(&u->reply_mutex);
+		ret = queue_reply(&staging_q, (char *)&hdr, sizeof(hdr));
+		if (!ret)
+			ret = queue_reply(&staging_q, (char *)XS_RESP, hdr.len);
+		if (!ret) {
+			list_splice_tail(&staging_q, &u->read_buffers);
+			wake_up(&u->read_waitq);
+		} else {
+			queue_cleanup(&staging_q);
+			rc = ret;
+		}
+		mutex_unlock(&u->reply_mutex);
+		break;
+	}

drivers/xen/xenfs/super.c

Quoted 10 lines
drivers/xen/xenfs/super.c new file +10 -0
+
+	default:
+		rc = -EINVAL;
+		break;
+	}
+
+ out:
+	u->len = 0;
+	return rc;
+}

This function implements a new kernel ABI. How are reviewers to review the design of this proposed ABI?

drivers/xen/xenfs/super.c

Quoted 10 lines

...

drivers/xen/xenfs/super.c new file +7 -0
+static unsigned int xenbus_file_poll(struct file *file, poll_table *wait)
+{
+	struct xenbus_file_priv *u = file->private_data;
+
+	poll_wait(file, &u->read_waitq, wait);
+	if (!list_empty(&u->read_buffers))
+		return POLLIN | POLLRDNORM;

I wonder if we needed the lock or some open-coded barrier to safely run list_empty().

Quoted 6 lines
drivers/xen/xenfs/super.c new file +3 -0
+	return 0;
+}
+
 
...
#3 lore raw
Quoted 41 lines · Andrew Morton
Quoted 3 lines · On Tue, 16 Dec 2008 12:27:37 -0800 Jeremy Fitzhardinge <[email protected]> wrote:

[ Reviewers: This is in drivers/xen to keep it close to the code it is and will be using. Would people
prefer to see it in fs/xenfs? -J ]

Well it would be nice to keep filesystems under ./fs, but `grep -rl register_filesystem .' says we screwed that pooch.

Quoted 29 lines

From: Alex Zeffertt <[email protected]>

The xenfs filesystem exports various interfaces to usermode. Initially this exports a file to allow usermode to interact with xenbus/xenstore.

Traditionally this appeared in /proc/xen. Rather than extending procfs, this patch adds a backward-compat mountpoint on /proc/xen, and provides a xenfs filesystem which can be mounted there.

...

drivers/xen/xenfs/super.c new file +14 -0
@@ -0,0 +1,65 @@
+/*
+ *  xenfs.c - a filesystem for passing info between the a domain and
+ *  the hypervisor.
+ *
+ * 2008-10-07  Alex Zeffertt    Replaced /proc/xen/xenbus with xenfs filesystem
+ *                              and /proc/xen compatibility mount point.
+ *                              Turned xenfs into a loadable module.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+
Quoted 9 lines

drivers/xen/xenfs/super.c line 19

Quoted 6 lines
drivers/xen/xenfs/super.c new file +5 -0
+#include "xenfs.h"
+
+#include <asm/xen/hypervisor.h>
+
+#define XENFS_MAGIC	0xabba1974

Move to include/linux/magic.h, please.

OK.

Quoted 7 lines

drivers/xen/xenfs/super.c line 22

Quoted 4 lines
drivers/xen/xenfs/super.c new file +2 -0
+MODULE_DESCRIPTION("Xen filesystem");
+MODULE_LICENSE("GPL");

MODULE_AUTHOR()?

There's at least 4 candidates for that role, and Citrix is the official copyright holder, so I'm not sure what I'd put there.

Quoted 2 lines

./MAINTAINERS?

Covered by the blanket Xen entry (which could do with a refresh).

Quoted 41 lines
Quoted 41 lines
drivers/xen/xenfs/super.c new file +36 -0
+static int xenfs_fill_super(struct super_block *sb, void *data, int silent)
+{
+	static struct tree_descr xenfs_files[] = {
+		[2] = {"xenbus", &xenbus_file_ops, S_IRUSR|S_IWUSR},
+		{""},
+	};
+
+	return simple_fill_super(sb, XENFS_MAGIC, xenfs_files);
+}
+
 
...
 
+struct xenbus_transaction_holder {
+	struct list_head list;
+	struct xenbus_transaction handle;
+};
+
+struct read_buffer {
+	struct list_head list;
+	unsigned int cons;
+	unsigned int len;
+	char msg[];
+};
 
+struct xenbus_file_priv {
+	/* In-progress transaction. */
+	struct list_head transactions;
+
+	/* Active watches. */
+	struct list_head watches;
+
+	/* Partial request. */
+	unsigned int len;
+	union {
+		struct xsd_sockmsg msg;
+		char buffer[PAGE_SIZE];
+	} u;
+
+	/* Response queue. */
Quoted 9 lines

drivers/xen/xenfs/super.c

Quoted 6 lines
drivers/xen/xenfs/super.c new file +5 -0
+	struct list_head read_buffers;
+	wait_queue_head_t read_waitq;
+
+	struct mutex reply_mutex;
+};

It would be useful to document the locking for the list_head's (at least).

Hm, and add some, by the looks of it.

Quoted 18 lines
Quoted 18 lines
drivers/xen/xenfs/super.c new file +18 -0
+
+static ssize_t xenbus_file_read(struct file *filp,
+			       char __user *ubuf,
+			       size_t len, loff_t *ppos)
+{
+	struct xenbus_file_priv *u = filp->private_data;
+	struct read_buffer *rb;
+	int i, ret;
+
+	mutex_lock(&u->reply_mutex);
+	while (list_empty(&u->read_buffers)) {
+		mutex_unlock(&u->reply_mutex);
+		ret = wait_event_interruptible(u->read_waitq,
+					       !list_empty(&u->read_buffers));
+		if (ret)
+			return ret;
+		mutex_lock(&u->reply_mutex);
+	}
Quoted 9 lines

drivers/xen/xenfs/super.c

Quoted 5 lines
drivers/xen/xenfs/super.c new file +4 -0
+
+	rb = list_entry(u->read_buffers.next, struct read_buffer, list);
+	for (i = 0; i < len;) {
+		ret = put_user(rb->msg[rb->cons], ubuf + i);

So mmap_sem nests inside ->reply_mutex. Has this all been carefully tested with lockdep?

I run with lockdep enabled habitually, and I've seen no squeaks from this code.

The mmap_sem nesting would be a problem if this even implemented mappable files, but it is OK now?

Quoted 51 lines
Quoted 7 lines
drivers/xen/xenfs/super.c new file +5 -0
+		if (ret) {
+			mutex_unlock(&u->reply_mutex);
+			return ret;
+		}
+		i++;

drivers/xen/xenfs/super.c

Quoted 10 lines
drivers/xen/xenfs/super.c new file +10 -0
+		rb->cons++;
+		if (rb->cons == rb->len) {
+			list_del(&rb->list);
+			kfree(rb);
+			if (list_empty(&u->read_buffers))
+				break;
+			rb = list_entry(u->read_buffers.next,
+					struct read_buffer, list);
+		}
+	}

This code will misbehave if it ever receives a read_buffer with len==0.

drivers/xen/xenfs/super.c

Quoted 12 lines
drivers/xen/xenfs/super.c new file +11 -0
+	mutex_unlock(&u->reply_mutex);
+
+	return i;
+}
+
+static int queue_reply(struct list_head *list, char *data, unsigned int len)
+{
+	struct read_buffer *rb;
+
+	if (len == 0)
+		return 0;

That should fix it.

drivers/xen/xenfs/super.c

Quoted 11 lines
drivers/xen/xenfs/super.c new file +10 -0
+	rb = kmalloc(sizeof(*rb) + len, GFP_KERNEL);
+	if (rb == NULL)
+		return -ENOMEM;
+
+	rb->cons = 0;
+	rb->len = len;
+
+	memcpy(rb->msg, data, len);
+
+	list_add_tail(&rb->list, list);

So the caller of this function must hold ->reply_mutex.

Quoted 2 lines

You thought that was secret but I found it out!

No, it needn't. The list its adding to is a local staging list, which is spliced to the real reply list once everything has been successfully allocated (otherwise it just gets thrown out). In practice all the callers to hold the ->reply_mutex.

Quoted 15 lines
Quoted 15 lines
drivers/xen/xenfs/super.c new file +14 -0
+	return 0;
+}
+
+/* Free all the read_buffer s on a list.
+ * Caller must have sole reference to list.
+ */
+static void queue_cleanup(struct list_head  *list)
+{
+	struct read_buffer *rb;
+
+	while (!list_empty(list)) {
+		rb = list_entry(list->next, struct read_buffer, list);
+		list_del(list->next);
+		kfree(rb);
Quoted 9 lines

drivers/xen/xenfs/super.c

Quoted 6 lines
drivers/xen/xenfs/super.c new file +5 -0
+	}
+}
+
+struct watch_adapter {
+	struct list_head list;

locking is?

Missing.

Quoted 31 lines
Quoted 18 lines
drivers/xen/xenfs/super.c new file +14 -0
+	struct xenbus_watch watch;
+	struct xenbus_file_priv *dev_data;
+	char *token;
+};
+
 
...
 
+static LIST_HEAD(watch_list);
+
+static ssize_t xenbus_file_write(struct file *filp,
+				const char __user *ubuf,
+				size_t len, loff_t *ppos)
+{
+	struct xenbus_file_priv *u = filp->private_data;
+	struct xenbus_transaction_holder *trans = NULL;
+	uint32_t msg_type;

drivers/xen/xenfs/super.c

Quoted 10 lines
drivers/xen/xenfs/super.c new file +10 -0
+	void *reply;
+	char *path, *token;
+	int err, rc = len;
+	int ret;
+	LIST_HEAD(staging_q);
+
+	if ((len + u->len) > sizeof(u->u.buffer)) {
+		rc = -EINVAL;
+		goto out;
+	}

Now what's this doing?

Quoted 5 lines

One would expect a large write to get chunked up into smaller writes by the kernel.

This code is poorly documented.

Each logical write is a xenbus message packet. If usermode passes a partial write then it gets accumulated until we have a full message.

Quoted 151 lines
Quoted 151 lines
drivers/xen/xenfs/super.c new file +150 -0
+	if (copy_from_user(u->u.buffer + u->len, ubuf, len) != 0) {
+		rc = -EFAULT;
+		goto out;
+	}
+
+	u->len += len;
+	if ((u->len < sizeof(u->u.msg)) ||
+	    (u->len < (sizeof(u->u.msg) + u->u.msg.len)))
+		return rc;
+
+	msg_type = u->u.msg.type;
+
+	switch (msg_type) {
+	case XS_TRANSACTION_START:
+	case XS_TRANSACTION_END:
+	case XS_DIRECTORY:
+	case XS_READ:
+	case XS_GET_PERMS:
+	case XS_RELEASE:
+	case XS_GET_DOMAIN_PATH:
+	case XS_WRITE:
+	case XS_MKDIR:
+	case XS_RM:
+	case XS_SET_PERMS:
+		if (msg_type == XS_TRANSACTION_START) {
+			trans = kmalloc(sizeof(*trans), GFP_KERNEL);
+			if (!trans) {
+				rc = -ENOMEM;
+				goto out;
+			}
+		}
+
+		reply = xenbus_dev_request_and_reply(&u->u.msg);
+		if (IS_ERR(reply)) {
+			kfree(trans);
+			rc = PTR_ERR(reply);
+			goto out;
+		}
+
+		if (msg_type == XS_TRANSACTION_START) {
+			trans->handle.id = simple_strtoul(reply, NULL, 0);
+			list_add(&trans->list, &u->transactions);
+		} else if (msg_type == XS_TRANSACTION_END) {
+			list_for_each_entry(trans, &u->transactions, list)
+				if (trans->handle.id == u->u.msg.tx_id)
+					break;
+			BUG_ON(&trans->list == &u->transactions);
+			list_del(&trans->list);
+			kfree(trans);
+		}
+		mutex_lock(&u->reply_mutex);
+		ret = queue_reply(&staging_q,
+				  (char *)&u->u.msg, sizeof(u->u.msg));
+		if (!ret)
+			ret = queue_reply(&staging_q,
+					  (char *)reply, u->u.msg.len);
+		if (!ret) {
+			list_splice_tail(&staging_q, &u->read_buffers);
+			wake_up(&u->read_waitq);
+		} else {
+			queue_cleanup(&staging_q);
+			rc = ret;
+		}
+		mutex_unlock(&u->reply_mutex);
+		kfree(reply);
+		break;
+
+	case XS_WATCH:
+	case XS_UNWATCH: {
+		struct watch_adapter *watch, *tmp_watch;
+		static const char XS_RESP[] = "OK";
+		struct xsd_sockmsg hdr;
+
+		path = u->u.buffer + sizeof(u->u.msg);
+		token = memchr(path, 0, u->u.msg.len);
+		if (token == NULL) {
+			rc = -EILSEQ;
+			goto out;
+		}
+		token++;
+
+		if (msg_type == XS_WATCH) {
+			watch = kzalloc(sizeof(*watch), GFP_KERNEL);
+			if (watch == NULL) {
+				rc = -ENOMEM;
+				goto out;
+			}
+			watch->watch.node =
+				kmalloc(strlen(path)+1, GFP_KERNEL);
+			if (watch->watch.node == NULL) {
+				kfree(watch);
+				rc = -ENOMEM;
+				goto out;
+			}
+			strcpy((char *)watch->watch.node, path);
+			watch->watch.callback = watch_fired;
+			watch->token =
+				kmalloc(strlen(token)+1, GFP_KERNEL);
+			if (watch->token == NULL) {
+				kfree(watch->watch.node);
+				kfree(watch);
+				rc = -ENOMEM;
+				goto out;
+			}
+			strcpy(watch->token, token);
+			watch->dev_data = u;
+
+			err = register_xenbus_watch(&watch->watch);
+			if (err) {
+				free_watch_adapter(watch);
+				rc = err;
+				goto out;
+			}
+
+			list_add(&watch->list, &u->watches);
+		} else {
+			list_for_each_entry_safe(watch, tmp_watch,
+						 &u->watches, list) {
+				if (!strcmp(watch->token, token) &&
+				    !strcmp(watch->watch.node, path)) {
+					unregister_xenbus_watch(&watch->watch);
+					list_del(&watch->list);
+					free_watch_adapter(watch);
+					break;
+				}
+			}
+		}
+
+		hdr.type = msg_type;
+		hdr.len = sizeof(XS_RESP);
+		mutex_lock(&u->reply_mutex);
+		ret = queue_reply(&staging_q, (char *)&hdr, sizeof(hdr));
+		if (!ret)
+			ret = queue_reply(&staging_q, (char *)XS_RESP, hdr.len);
+		if (!ret) {
+			list_splice_tail(&staging_q, &u->read_buffers);
+			wake_up(&u->read_waitq);
+		} else {
+			queue_cleanup(&staging_q);
+			rc = ret;
+		}
+		mutex_unlock(&u->reply_mutex);
+		break;
+	}
+
+	default:
+		rc = -EINVAL;
+		break;
+	}
+
Quoted 9 lines

drivers/xen/xenfs/super.c

Quoted 5 lines

+ out:

drivers/xen/xenfs/super.c new file +3 -0
+	u->len = 0;
+	return rc;
+}

This function implements a new kernel ABI. How are reviewers to review the design of this proposed ABI?

It's closer to a network protocol. This is pretty much a raw interface to the xenbus protocol allowing guests to access the host xenbus namespace. The read side just returns the naked xenbus protocol data. The write side needs to peek into it to maintain some local state, like what transactions are currently going. But in general the kernel doesn't inspect what's being written particularly closely.

Quoted 14 lines

drivers/xen/xenfs/super.c

Quoted 10 lines

...

drivers/xen/xenfs/super.c new file +7 -0
+static unsigned int xenbus_file_poll(struct file *file, poll_table *wait)
+{
+	struct xenbus_file_priv *u = file->private_data;
+
+	poll_wait(file, &u->read_waitq, wait);
+	if (!list_empty(&u->read_buffers))
+		return POLLIN | POLLRDNORM;

I wonder if we needed the lock or some open-coded barrier to safely run list_empty().

Not sure.

Thanks for looking over this. I'll post an updated patch after a quick round of testing.

J

#4 lore raw

PATCH 1/1 xen: add xenfs to allow usermode <-> Xen interaction

In reply to Andrew Morton

From: Alex Zeffertt <[email protected]>

The xenfs filesystem exports various interfaces to usermode. Initially this exports a file to allow usermode to interact with xenbus/xenstore.

Traditionally this appeared in /proc/xen. Rather than extending procfs, this patch adds a backward-compat mountpoint on /proc/xen, and provides a xenfs filesystem which can be mounted there.

[
I did quite a lot of work to this code as a result of review, which is why this is a repost rather than a delta. The changes are:

- Moved the XENFS_SUPER_MAGIC to linux/magic.h
- Added comments to answer the "what's this for?" questions (I hope)
- Split things out into smaller functions
- Cleaned up type of queue_reply(), removed casts
- Added a mutex to protect struct xenbus_file_priv. This protects the list heads, and the partial message buffer. There were several ways in which usermode could overwrite the kernel's memory via races without this locking.
- Fixed a bug in which usermode could start sending a message which can never be sent, leaving the file descriptor in a useless state.

I still don't know whether the list_empty test in xenbus_file_poll() needs a barrier.

-J ]

---

drivers/xen/Kconfig               |   24 +
drivers/xen/Makefile              |    2 drivers/xen/xenbus/xenbus_probe.c |   28 +
drivers/xen/xenbus/xenbus_xs.c    |    1 drivers/xen/xenfs/Makefile        |    3 drivers/xen/xenfs/super.c         |   64 +++
drivers/xen/xenfs/xenbus.c        |  593 +++++++++++++++++++++++++++++++++++++
drivers/xen/xenfs/xenfs.h         |    6 include/linux/magic.h             |    2 include/xen/xenbus.h              |    2 10 files changed, 717 insertions(+), 8 deletions(-)

===================================================================

drivers/xen/Kconfig +0 -0
@@ -17,3 +17,27 @@

is not accidentally visible to other domains. Is it more secure, but slightly less efficient.
If in doubt, say yes.
+
+config XENFS
+ tristate "Xen filesystem"
+ depends on XEN
+ default y
+ help

+	  The xen filesystem provides a way for domains to share
+	  information with each other and with the hypervisor.
+	  For example, by reading and writing the "xenbus" file, guests
+	  may pass arbitrary information to the initial domain.
+	  If in doubt, say yes.
+
+config XEN_COMPAT_XENFS
+       bool "Create compatibility mount point /proc/xen"
+       depends on XENFS
+       default y
+       help
+         The old xenstore userspace tools expect to find "xenbus"
+         under /proc/xen, but "xenbus" is now found at the root of the
+         xenfs filesystem.  Selecting this causes the kernel to create
+         the compatibilty mount point /proc/xen if it is running on
+         a xen platform.
+         If in doubt, say yes.

+
===================================================================

drivers/xen/Makefile +0 -0
@@ -1,5 +1,7 @@

obj-y += grant-table.o features.o events.o manage.o
obj-y += xenbus/
+
obj-$(CONFIG_HOTPLUG_CPU) += cpu_hotplug.o
obj-$(CONFIG_XEN_XENCOMM) += xencomm.o
obj-$(CONFIG_XEN_BALLOON) += balloon.o
+obj-$(CONFIG_XENFS) += xenfs/
\ No newline at end of file
===================================================================

drivers/xen/xenbus/xenbus_probe.c +0 -0
@@ -40,6 +40,7 @@

#include <linux/ctype.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
+#include <linux/proc_fs.h>
#include <linux/notifier.h>
#include <linux/kthread.h>
#include <linux/mutex.h>
@@ -55,7 +56,10 @@
#include "xenbus_comms.h"
#include "xenbus_probe.h"

+
int xen_store_evtchn; +EXPORT_SYMBOL(xen_store_evtchn);
+
struct xenstore_domain_interface *xen_store_interface; static unsigned long xen_store_mfn;

@@ -166,6 +170,9 @@

	return read_otherend_details(xendev, "backend-id", "backend");
}

+static struct device_attribute xenbus_dev_attrs[] = {
+ __ATTR_NULL
+};

/* Bus type for frontend drivers. */
static struct xen_bus_type xenbus_frontend = {
@@ -174,12 +181,13 @@
.get_bus_id = frontend_bus_id,
.probe = xenbus_probe_frontend,
.bus = {

-		.name     = "xen",
-		.match    = xenbus_match,
-		.uevent   = xenbus_uevent,
-		.probe    = xenbus_dev_probe,
-		.remove   = xenbus_dev_remove,
-		.shutdown = xenbus_dev_shutdown,
+		.name      = "xen",
+		.match     = xenbus_match,
+		.uevent    = xenbus_uevent,
+		.probe     = xenbus_dev_probe,
+		.remove    = xenbus_dev_remove,
+		.shutdown  = xenbus_dev_shutdown,
+		.dev_attrs = xenbus_dev_attrs,
	},
};

@@ -849,6 +857,14 @@

	if (!xen_initial_domain())
		xenbus_probe(NULL);

+#ifdef CONFIG_XEN_COMPAT_XENFS
+ /*
+ * Create xenfs mountpoint in /proc for compatibility with
+ * utilities that expect to find "xenbus" under "/proc/xen".
+ */
+ proc_mkdir("xen", NULL);
+#endif
+

	return 0;

out_unreg_back:
===================================================================

drivers/xen/xenbus/xenbus_xs.c +0 -0
@@ -184,6 +184,7 @@
 
	return ret;
}

+EXPORT_SYMBOL(xenbus_dev_request_and_reply);

/* Send message to xs, get kmalloc'ed reply. ERR_PTR() on error. */ static void *xs_talkv(struct xenbus_transaction t,
===================================================================

drivers/xen/xenfs/Makefile new file +3 -0
@@ -0,0 +1,3 @@
+obj-$(CONFIG_XENFS) += xenfs.o
+
+xenfs-objs = super.o xenbus.o
\ No newline at end of file

===================================================================

drivers/xen/xenfs/super.c new file +64 -0

64 lines not on this page · raw message

===================================================================

drivers/xen/xenfs/xenbus.c new file +593 -0

593 lines not on this page · raw message

===================================================================

drivers/xen/xenfs/xenfs.h new file +6 -0
@@ -0,0 +1,6 @@
+#ifndef _XENFS_XENBUS_H
+#define _XENFS_XENBUS_H
+
+extern const struct file_operations xenbus_file_ops;
+
+#endif	/* _XENFS_XENBUS_H */

===================================================================

include/linux/magic.h +0 -0
@@ -46,5 +46,7 @@

#define FUTEXFS_SUPER_MAGIC 0xBAD1DEA
#define INOTIFYFS_SUPER_MAGIC 0x2BAD1DEA

+#define XENFS_SUPER_MAGIC 0xabba1974
+
#define STACK_END_MAGIC 0x57AC6E9D
#endif /* __LINUX_MAGIC_H__ */
===================================================================

include/xen/xenbus.h +0 -0
@@ -136,8 +136,6 @@

/* Nil transaction ID. */
#define XBT_NIL ((struct xenbus_transaction) { 0 })

-int __init xenbus_dev_init(void);
-
char **xenbus_directory(struct xenbus_transaction t,

			const char *dir, const char *node, unsigned int *num);

void *xenbus_read(struct xenbus_transaction t,

#5 lore raw
Quoted 11 lines · On Wed, 17 Dec 2008 13:24:39 -0800 Jeremy Fitzhardinge <[email protected]> wrote:

The xenfs filesystem exports various interfaces to usermode. Initially this exports a file to allow usermode to interact with xenbus/xenstore.

Traditionally this appeared in /proc/xen. Rather than extending procfs, this patch adds a backward-compat mountpoint on /proc/xen, and provides a xenfs filesystem which can be mounted there.

[
I did quite a lot of work to this code as a result of review, which is why this is a repost rather than a delta. The changes are:

Quoted 11 lines

- Moved the XENFS_SUPER_MAGIC to linux/magic.h
- Added comments to answer the "what's this for?" questions (I hope)
- Split things out into smaller functions
- Cleaned up type of queue_reply(), removed casts
- Added a mutex to protect struct xenbus_file_priv. This protects the list heads, and the partial message buffer. There were several ways in which usermode could overwrite the kernel's memory via races without this locking.
- Fixed a bug in which usermode could start sending a message which can never be sent, leaving the file descriptor in a useless state.

I'm bored of your filesystem, so I think I'll just merge it, unless someone else wants to ;)

Was this noticed and considered?

WARNING: consider using strict_strtoul in preference to simple_strtoul #543: FILE: drivers/xen/xenfs/xenbus.c:315:
+ trans->handle.id = simple_strtoul(reply, NULL, 0);

total: 0 errors, 1 warnings, 781 lines checked

#6 lore raw
Quoted 3 lines · Andrew Morton

I'm bored of your filesystem, so I think I'll just merge it, unless someone else wants to ;)

Yay! Boring code Wins!

Quoted 6 lines

Was this noticed and considered?

WARNING: consider using strict_strtoul in preference to simple_strtoul #543: FILE: drivers/xen/xenfs/xenbus.c:315:
+ trans->handle.id = simple_strtoul(reply, NULL, 0);

Noticed that, but wasn't sure how to handle an error there anyway. Something pretty horrible must have happened at the other end.

J

#7 lore raw
In reply to Andrew Morton
Quoted 26 lines · Andrew Morton
Quoted 11 lines · On Wed, 17 Dec 2008 13:24:39 -0800 Jeremy Fitzhardinge <[email protected]> wrote:

The xenfs filesystem exports various interfaces to usermode. Initially this exports a file to allow usermode to interact with xenbus/xenstore.

Traditionally this appeared in /proc/xen. Rather than extending procfs, this patch adds a backward-compat mountpoint on /proc/xen, and provides a xenfs filesystem which can be mounted there.

[
I did quite a lot of work to this code as a result of review, which is why this is a repost rather than a delta. The changes are:

Quoted 11 lines

- Moved the XENFS_SUPER_MAGIC to linux/magic.h
- Added comments to answer the "what's this for?" questions (I hope)
- Split things out into smaller functions
- Cleaned up type of queue_reply(), removed casts
- Added a mutex to protect struct xenbus_file_priv. This protects the list heads, and the partial message buffer. There were several ways in which usermode could overwrite the kernel's memory via races without this locking.
- Fixed a bug in which usermode could start sending a message which can never be sent, leaving the file descriptor in a useless state.

Quoted 2 lines

I'm bored of your filesystem, so I think I'll just merge it, unless someone else wants to ;)

please do - there should be no big overlap with existing other Xen bits in tip/*.

Ingo

#8 lore raw

Re: [PATCH UPDATED] xen/xenfs: fix xenbus message reads

In reply to Jeremy Fitzhardinge

Compute the remaining data size properly.

---
drivers/xen/xenfs/xenbus.c | 2 +-

1 file changed, 1 insertion(+), 1 deletion(-)

===================================================================

drivers/xen/xenfs/xenbus.c +0 -0
@@ -132,7 +132,7 @@
	rb = list_entry(u->read_buffers.next, struct read_buffer, list);
	i = 0;
	while (i < len) {

- unsigned sz = min((unsigned)len - i, rb->cons - rb->len);
+ unsigned sz = min((unsigned)len - i, rb->len - rb->cons);

		ret = copy_to_user(ubuf + i, &rb->msg[rb->cons], sz);