/* * Driver giving user-space access to the kernel's xenbus connection * to xenstore. * * Copyright (c) 2005, Christian Limpach * Copyright (c) 2005, Rusty Russell, IBM Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation; or, when distributed * separately from the Linux kernel or incorporated into other * software packages, subject to the following license: * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this source file (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. * * Changes: * 2008-10-07 Alex Zeffertt Replaced /proc/xen/xenbus with xenfs filesystem * and /proc/xen compatibility mount point. * Turned xenfs into a loadable module.
*/
/* * An element of a list of outstanding transactions, for which we're * still waiting a reply.
*/ struct xenbus_transaction_holder { struct list_head list; struct xenbus_transaction handle; unsignedint generation_id;
};
/* * A buffer of data on the queue.
*/ struct read_buffer { struct list_head list; unsignedint cons; unsignedint len; char msg[] __counted_by(len);
};
struct xenbus_file_priv { /* * msgbuffer_mutex is held while partial requests are built up * and complete requests are acted on. It therefore protects * the "transactions" and "watches" lists, and the partial * request length and buffer. * * reply_mutex protects the reply being built up to return to * usermode. It nests inside msgbuffer_mutex but may be held * alone during a watch callback.
*/ struct mutex msgbuffer_mutex;
/* Read out any raw xenbus messages queued up. */ 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;
ssize_t i; int ret;
mutex_lock(&u->reply_mutex);
again: while (list_empty(&u->read_buffers)) {
mutex_unlock(&u->reply_mutex); if (filp->f_flags & O_NONBLOCK) return -EAGAIN;
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);
i = 0; while (i < len) {
size_t sz = min_t(size_t, len - i, rb->len - rb->cons);
ret = copy_to_user(ubuf + i, &rb->msg[rb->cons], sz);
i += sz - ret;
rb->cons += sz - ret;
if (ret != 0) { if (i == 0)
i = -EFAULT; goto out;
}
/* Clear out buffer if it has been consumed */ 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);
}
} if (i == 0) goto again;
out:
mutex_unlock(&u->reply_mutex); return i;
}
/* * Add a buffer to the queue. Caller must hold the appropriate lock * if the queue is not local. (Commonly the caller will build up * multiple queued buffers on a temporary local list, and then add it * to the appropriate list under lock once all the buffers have een * successfully allocated.)
*/ staticint queue_reply(struct list_head *queue, constvoid *data, size_t len)
{ struct read_buffer *rb;
if (len == 0) return 0; if (len > XENSTORE_PAYLOAD_MAX) return -EINVAL;
/* * Free all the read_buffer s on a list. * Caller must have sole reference to list.
*/ staticvoid queue_cleanup(struct list_head *list)
{ struct read_buffer *rb;
ret = queue_reply(&staging_q, &hdr, sizeof(hdr)); if (!ret)
ret = queue_reply(&staging_q, path, path_len); if (!ret)
ret = queue_reply(&staging_q, token_caller, tok_len);
if (!ret) { /* success: pass reply list onto watcher */
list_splice_tail(&staging_q, &adap->dev_data->read_buffers);
wake_up(&adap->dev_data->read_waitq);
} else
queue_cleanup(&staging_q);
/* * We might be called in xenbus_thread(). * Use workqueue to avoid deadlock.
*/
u = container_of(kref, struct xenbus_file_priv, kref);
schedule_work(&u->wq);
}
/* * We're expecting usermode to be writing properly formed * xenbus messages. If they write an incomplete message we * buffer it up. Once it is complete, we act on it.
*/
/* * Make sure concurrent writers can't stomp all over each * other's messages and make a mess of our partial message * buffer. We don't make any attemppt to stop multiple * writers from making a mess of each other's incomplete * messages; we're just trying to guarantee our own internal * consistency and make sure that single writes are handled * atomically.
*/
mutex_lock(&u->msgbuffer_mutex);
/* Get this out of the way early to avoid confusion */ if (len == 0) goto out;
/* Can't write a xenbus message larger we can buffer */ if (len > sizeof(u->u.buffer) - u->len) { /* On error, dump existing buffer */
u->len = 0;
rc = -EINVAL; goto out;
}
ret = copy_from_user(u->u.buffer + u->len, ubuf, len);
if (ret != 0) {
rc = -EFAULT; goto out;
}
/* Deal with a partial copy. */
len -= ret;
rc = len;
u->len += len;
/* Return if we haven't got a full message yet */ if (u->len < sizeof(u->u.msg)) goto out; /* not even the header yet */
/* If we're expecting a message that's larger than we can
possibly send, dump what we have and return an error. */ if ((sizeof(u->u.msg) + u->u.msg.len) > sizeof(u->u.buffer)) {
rc = -E2BIG;
u->len = 0; goto out;
}
if (u->len < (sizeof(u->u.msg) + u->u.msg.len)) goto out; /* incomplete data portion */
/* * OK, now we have a complete message. Do something with it.
*/
kref_get(&u->kref);
msg_type = u->u.msg.type;
switch (msg_type) { case XS_WATCH: case XS_UNWATCH: /* (Un)Ask for some path to be watched for changes */
ret = xenbus_write_watch(msg_type, u); break;
default: /* Send out a transaction */
ret = xenbus_write_transaction(msg_type, u); break;
} if (ret != 0) {
rc = ret;
kref_put(&u->kref, xenbus_file_free);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.