cregit-Linux how code gets into the kernel

Release 4.11 drivers/xen/xenbus/xenbus_dev_frontend.c

/*
 * 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.
 */


#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/uio.h>
#include <linux/notifier.h>
#include <linux/wait.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/mount.h>
#include <linux/pagemap.h>
#include <linux/uaccess.h>
#include <linux/init.h>
#include <linux/namei.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/miscdevice.h>

#include <xen/xenbus.h>
#include <xen/xen.h>
#include <asm/xen/hypervisor.h>

#include "xenbus.h"

/*
 * 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;
};

/*
 * A buffer of data on the queue.
 */

struct read_buffer {
	
struct list_head list;
	
unsigned int cons;
	
unsigned int len;
	
char msg[];
};


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;

	/* In-progress transactions */
	
struct list_head transactions;

	/* Active watches. */
	
struct list_head watches;

	/* Partial request. */
	
unsigned int len;
	union {
		
struct xsd_sockmsg msg;
		
char buffer[XENSTORE_PAYLOAD_MAX];
	} 
u;

	/* Response queue. */
	
struct mutex reply_mutex;
	
struct list_head read_buffers;
	
wait_queue_head_t read_waitq;

	
struct kref kref;
};

/* 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; unsigned 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) { unsigned sz = min((unsigned)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; }

Contributors

PersonTokensPropCommitsCommitProp
Alex Zeffertt27591.97%125.00%
Paolo Bonzini124.01%125.00%
Daniel De Graaf113.68%125.00%
Jeremy Fitzhardinge10.33%125.00%
Total299100.00%4100.00%

/* * 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.) */
static int queue_reply(struct list_head *queue, const void *data, size_t len) { struct read_buffer *rb; if (len == 0) return 0; if (len > XENSTORE_PAYLOAD_MAX) return -EINVAL; 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, queue); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alex Zeffertt9490.38%150.00%
Insu Yun109.62%150.00%
Total104100.00%2100.00%

/* * 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); } }

Contributors

PersonTokensPropCommitsCommitProp
Alex Zeffertt52100.00%1100.00%
Total52100.00%1100.00%

struct watch_adapter { struct list_head list; struct xenbus_watch watch; struct xenbus_file_priv *dev_data; char *token; };
static void free_watch_adapter(struct watch_adapter *watch) { kfree(watch->watch.node); kfree(watch->token); kfree(watch); }

Contributors

PersonTokensPropCommitsCommitProp
Alex Zeffertt32100.00%1100.00%
Total32100.00%1100.00%


static struct watch_adapter *alloc_watch_adapter(const char *path, const char *token) { struct watch_adapter *watch; watch = kzalloc(sizeof(*watch), GFP_KERNEL); if (watch == NULL) goto out_fail; watch->watch.node = kstrdup(path, GFP_KERNEL); if (watch->watch.node == NULL) goto out_free; watch->token = kstrdup(token, GFP_KERNEL); if (watch->token == NULL) goto out_free; return watch; out_free: free_watch_adapter(watch); out_fail: return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Alex Zeffertt108100.00%1100.00%
Total108100.00%1100.00%


static void watch_fired(struct xenbus_watch *watch, const char *path, const char *token) { struct watch_adapter *adap; struct xsd_sockmsg hdr; const char *token_caller; int path_len, tok_len, body_len; int ret; LIST_HEAD(staging_q); adap = container_of(watch, struct watch_adapter, watch); token_caller = adap->token; path_len = strlen(path) + 1; tok_len = strlen(token_caller) + 1; body_len = path_len + tok_len; hdr.type = XS_WATCH_EVENT; hdr.len = body_len; mutex_lock(&adap->dev_data->reply_mutex); 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); mutex_unlock(&adap->dev_data->reply_mutex); }

Contributors

PersonTokensPropCommitsCommitProp
Alex Zeffertt20395.75%150.00%
Juergen Gross94.25%150.00%
Total212100.00%2100.00%


static void xenbus_file_free(struct kref *kref) { struct xenbus_file_priv *u; struct xenbus_transaction_holder *trans, *tmp; struct watch_adapter *watch, *tmp_watch; struct read_buffer *rb, *tmp_rb; u = container_of(kref, struct xenbus_file_priv, kref); /* * No need for locking here because there are no other users, * by definition. */ list_for_each_entry_safe(trans, tmp, &u->transactions, list) { xenbus_transaction_end(trans->handle, 1); list_del(&trans->list); kfree(trans); } list_for_each_entry_safe(watch, tmp_watch, &u->watches, list) { unregister_xenbus_watch(&watch->watch); list_del(&watch->list); free_watch_adapter(watch); } list_for_each_entry_safe(rb, tmp_rb, &u->read_buffers, list) { list_del(&rb->list); kfree(rb); } kfree(u); }

Contributors

PersonTokensPropCommitsCommitProp
Juergen Gross150100.00%2100.00%
Total150100.00%2100.00%


static struct xenbus_transaction_holder *xenbus_get_transaction( struct xenbus_file_priv *u, uint32_t tx_id) { struct xenbus_transaction_holder *trans; list_for_each_entry(trans, &u->transactions, list) if (trans->handle.id == tx_id) return trans; return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Juergen Gross2555.56%150.00%
Alex Zeffertt2044.44%150.00%
Total45100.00%2100.00%


void xenbus_dev_queue_reply(struct xb_req_data *req) { struct xenbus_file_priv *u = req->par; struct xenbus_transaction_holder *trans = NULL; int rc; LIST_HEAD(staging_q); xs_request_exit(req); mutex_lock(&u->msgbuffer_mutex); if (req->type == XS_TRANSACTION_START) { trans = xenbus_get_transaction(u, 0); if (WARN_ON(!trans)) goto out; if (req->msg.type == XS_ERROR) { list_del(&trans->list); kfree(trans); } else { rc = kstrtou32(req->body, 10, &trans->handle.id); if (WARN_ON(rc)) goto out; } } else if (req->msg.type == XS_TRANSACTION_END) { trans = xenbus_get_transaction(u, req->msg.tx_id); if (WARN_ON(!trans)) goto out; list_del(&trans->list); kfree(trans); } mutex_unlock(&u->msgbuffer_mutex); mutex_lock(&u->reply_mutex); rc = queue_reply(&staging_q, &req->msg, sizeof(req->msg)); if (!rc) rc = queue_reply(&staging_q, req->body, req->msg.len); if (!rc) { list_splice_tail(&staging_q, &u->read_buffers); wake_up(&u->read_waitq); } else { queue_cleanup(&staging_q); } mutex_unlock(&u->reply_mutex); kfree(req->body); kfree(req); kref_put(&u->kref, xenbus_file_free); return; out: mutex_unlock(&u->msgbuffer_mutex); }

Contributors

PersonTokensPropCommitsCommitProp
Juergen Gross25580.19%350.00%
Alex Zeffertt3310.38%116.67%
Jan Beulich309.43%233.33%
Total318100.00%6100.00%


static int xenbus_command_reply(struct xenbus_file_priv *u, unsigned int msg_type, const char *reply) { struct { struct xsd_sockmsg hdr; const char body[16]; } msg; int rc; msg.hdr = u->u.msg; msg.hdr.type = msg_type; msg.hdr.len = strlen(reply) + 1; if (msg.hdr.len > sizeof(msg.body)) return -E2BIG; mutex_lock(&u->reply_mutex); rc = queue_reply(&u->read_buffers, &msg, sizeof(msg.hdr) + msg.hdr.len); wake_up(&u->read_waitq); mutex_unlock(&u->reply_mutex); if (!rc) kref_put(&u->kref, xenbus_file_free); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Juergen Gross14089.17%133.33%
Jennifer Herbert117.01%133.33%
Alex Zeffertt63.82%133.33%
Total157100.00%3100.00%


static int xenbus_write_transaction(unsigned msg_type, struct xenbus_file_priv *u) { int rc; struct xenbus_transaction_holder *trans = NULL; if (msg_type == XS_TRANSACTION_START) { trans = kzalloc(sizeof(*trans), GFP_KERNEL); if (!trans) { rc = -ENOMEM; goto out; } list_add(&trans->list, &u->transactions); } else if (u->u.msg.tx_id != 0 && !xenbus_get_transaction(u, u->u.msg.tx_id)) return xenbus_command_reply(u, XS_ERROR, "ENOENT"); rc = xenbus_dev_request_and_reply(&u->u.msg, u); if (rc && trans) { list_del(&trans->list); kfree(trans); } out: return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Juergen Gross7550.00%120.00%
Alex Zeffertt5536.67%120.00%
Jan Beulich128.00%120.00%
Jennifer Herbert64.00%120.00%
Ian Campbell21.33%120.00%
Total150100.00%5100.00%


static int xenbus_write_watch(unsigned msg_type, struct xenbus_file_priv *u) { struct watch_adapter *watch; char *path, *token; int err, rc; LIST_HEAD(staging_q); path = u->u.buffer + sizeof(u->u.msg); token = memchr(path, 0, u->u.msg.len); if (token == NULL) { rc = xenbus_command_reply(u, XS_ERROR, "EINVAL"); goto out; } token++; if (memchr(token, 0, u->u.msg.len - (token - path)) == NULL) { rc = xenbus_command_reply(u, XS_ERROR, "EINVAL"); goto out; } if (msg_type == XS_WATCH) { watch = alloc_watch_adapter(path, token); if (watch == NULL) { rc = -ENOMEM; goto out; } watch->watch.callback = watch_fired; 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(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; } } } /* Success. Synthesize a reply to say all is OK. */ rc = xenbus_command_reply(u, msg_type, "OK"); out: return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Alex Zeffertt24982.45%125.00%
Jan Beulich3411.26%250.00%
Juergen Gross196.29%125.00%
Total302100.00%4100.00%


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; uint32_t msg_type; int rc = len; int ret; LIST_HEAD(staging_q); /* * 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); } /* Buffered message consumed */ u->len = 0; out: mutex_unlock(&u->msgbuffer_mutex); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Alex Zeffertt31390.46%120.00%
Juergen Gross205.78%120.00%
Diego Ongaro72.02%120.00%
Jan Beulich41.16%120.00%
Jeremy Fitzhardinge20.58%120.00%
Total346100.00%5100.00%


static int xenbus_file_open(struct inode *inode, struct file *filp) { struct xenbus_file_priv *u; if (xen_store_evtchn == 0) return -ENOENT; nonseekable_open(inode, filp); filp->f_mode &= ~FMODE_ATOMIC_POS; /* cdev-style semantics */ u = kzalloc(sizeof(*u), GFP_KERNEL); if (u == NULL) return -ENOMEM; kref_init(&u->kref); INIT_LIST_HEAD(&u->transactions); INIT_LIST_HEAD(&u->watches); INIT_LIST_HEAD(&u->read_buffers); init_waitqueue_head(&u->read_waitq); mutex_init(&u->reply_mutex); mutex_init(&u->msgbuffer_mutex); filp->private_data = u; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alex Zeffertt11888.06%133.33%
Juergen Gross85.97%133.33%
David Vrabel85.97%133.33%
Total134100.00%3100.00%


static int xenbus_file_release(struct inode *inode, struct file *filp) { struct xenbus_file_priv *u = filp->private_data; kref_put(&u->kref, xenbus_file_free); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alex Zeffertt3386.84%133.33%
Juergen Gross410.53%133.33%
Daniel De Graaf12.63%133.33%
Total38100.00%3100.00%


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; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alex Zeffertt56100.00%1100.00%
Total56100.00%1100.00%

const struct file_operations xen_xenbus_fops = { .read = xenbus_file_read, .write = xenbus_file_write, .open = xenbus_file_open, .release = xenbus_file_release, .poll = xenbus_file_poll, .llseek = no_llseek, }; EXPORT_SYMBOL_GPL(xen_xenbus_fops); static struct miscdevice xenbus_dev = { .minor = MISC_DYNAMIC_MINOR, .name = "xen/xenbus", .fops = &xen_xenbus_fops, };
static int __init xenbus_init(void) { int err; if (!xen_domain()) return -ENODEV; err = misc_register(&xenbus_dev); if (err) pr_err("Could not register xenbus frontend device\n"); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Bastian Blank4197.62%266.67%
Joe Perches12.38%133.33%
Total42100.00%3100.00%

device_initcall(xenbus_init);

Overall Contributors

PersonTokensPropCommitsCommitProp
Alex Zeffertt184465.72%13.33%
Juergen Gross71325.41%516.67%
Jan Beulich802.85%620.00%
Bastian Blank752.67%26.67%
Jennifer Herbert170.61%13.33%
Daniel De Graaf120.43%26.67%
Paolo Bonzini120.43%13.33%
Insu Yun100.36%13.33%
Joe Perches80.29%13.33%
David Vrabel80.29%13.33%
Diego Ongaro70.25%13.33%
Arnd Bergmann50.18%13.33%
Alexey Dobriyan30.11%13.33%
Jeremy Fitzhardinge30.11%13.33%
Ian Campbell30.11%26.67%
Tejun Heo30.11%13.33%
Konrad Rzeszutek Wilk20.07%13.33%
Paul Gortmaker10.04%13.33%
Total2806100.00%30100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.