xenbus_xs.c
来自「linux 内核源代码」· C语言 代码 · 共 862 行 · 第 1/2 页
C
862 行
/****************************************************************************** * xenbus_xs.c * * This is the kernel equivalent of the "xs" library. We don't need everything * and we use xenbus_comms for communication. * * 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. */#include <linux/unistd.h>#include <linux/errno.h>#include <linux/types.h>#include <linux/uio.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/err.h>#include <linux/slab.h>#include <linux/fcntl.h>#include <linux/kthread.h>#include <linux/rwsem.h>#include <linux/module.h>#include <linux/mutex.h>#include <xen/xenbus.h>#include "xenbus_comms.h"struct xs_stored_msg { struct list_head list; struct xsd_sockmsg hdr; union { /* Queued replies. */ struct { char *body; } reply; /* Queued watch events. */ struct { struct xenbus_watch *handle; char **vec; unsigned int vec_size; } watch; } u;};struct xs_handle { /* A list of replies. Currently only one will ever be outstanding. */ struct list_head reply_list; spinlock_t reply_lock; wait_queue_head_t reply_waitq; /* * Mutex ordering: transaction_mutex -> watch_mutex -> request_mutex. * response_mutex is never taken simultaneously with the other three. */ /* One request at a time. */ struct mutex request_mutex; /* Protect xenbus reader thread against save/restore. */ struct mutex response_mutex; /* Protect transactions against save/restore. */ struct rw_semaphore transaction_mutex; /* Protect watch (de)register against save/restore. */ struct rw_semaphore watch_mutex;};static struct xs_handle xs_state;/* List of registered watches, and a lock to protect it. */static LIST_HEAD(watches);static DEFINE_SPINLOCK(watches_lock);/* List of pending watch callback events, and a lock to protect it. */static LIST_HEAD(watch_events);static DEFINE_SPINLOCK(watch_events_lock);/* * Details of the xenwatch callback kernel thread. The thread waits on the * watch_events_waitq for work to do (queued on watch_events list). When it * wakes up it acquires the xenwatch_mutex before reading the list and * carrying out work. */static pid_t xenwatch_pid;static DEFINE_MUTEX(xenwatch_mutex);static DECLARE_WAIT_QUEUE_HEAD(watch_events_waitq);static int get_error(const char *errorstring){ unsigned int i; for (i = 0; strcmp(errorstring, xsd_errors[i].errstring) != 0; i++) { if (i == ARRAY_SIZE(xsd_errors) - 1) { printk(KERN_WARNING "XENBUS xen store gave: unknown error %s", errorstring); return EINVAL; } } return xsd_errors[i].errnum;}static void *read_reply(enum xsd_sockmsg_type *type, unsigned int *len){ struct xs_stored_msg *msg; char *body; spin_lock(&xs_state.reply_lock); while (list_empty(&xs_state.reply_list)) { spin_unlock(&xs_state.reply_lock); /* XXX FIXME: Avoid synchronous wait for response here. */ wait_event(xs_state.reply_waitq, !list_empty(&xs_state.reply_list)); spin_lock(&xs_state.reply_lock); } msg = list_entry(xs_state.reply_list.next, struct xs_stored_msg, list); list_del(&msg->list); spin_unlock(&xs_state.reply_lock); *type = msg->hdr.type; if (len) *len = msg->hdr.len; body = msg->u.reply.body; kfree(msg); return body;}void *xenbus_dev_request_and_reply(struct xsd_sockmsg *msg){ void *ret; struct xsd_sockmsg req_msg = *msg; int err; if (req_msg.type == XS_TRANSACTION_START) down_read(&xs_state.transaction_mutex); mutex_lock(&xs_state.request_mutex); err = xb_write(msg, sizeof(*msg) + msg->len); if (err) { msg->type = XS_ERROR; ret = ERR_PTR(err); } else ret = read_reply(&msg->type, &msg->len); mutex_unlock(&xs_state.request_mutex); if ((msg->type == XS_TRANSACTION_END) || ((req_msg.type == XS_TRANSACTION_START) && (msg->type == XS_ERROR))) up_read(&xs_state.transaction_mutex); return ret;}/* Send message to xs, get kmalloc'ed reply. ERR_PTR() on error. */static void *xs_talkv(struct xenbus_transaction t, enum xsd_sockmsg_type type, const struct kvec *iovec, unsigned int num_vecs, unsigned int *len){ struct xsd_sockmsg msg; void *ret = NULL; unsigned int i; int err; msg.tx_id = t.id; msg.req_id = 0; msg.type = type; msg.len = 0; for (i = 0; i < num_vecs; i++) msg.len += iovec[i].iov_len; mutex_lock(&xs_state.request_mutex); err = xb_write(&msg, sizeof(msg)); if (err) { mutex_unlock(&xs_state.request_mutex); return ERR_PTR(err); } for (i = 0; i < num_vecs; i++) { err = xb_write(iovec[i].iov_base, iovec[i].iov_len); if (err) { mutex_unlock(&xs_state.request_mutex); return ERR_PTR(err); } } ret = read_reply(&msg.type, len); mutex_unlock(&xs_state.request_mutex); if (IS_ERR(ret)) return ret; if (msg.type == XS_ERROR) { err = get_error(ret); kfree(ret); return ERR_PTR(-err); } if (msg.type != type) { if (printk_ratelimit()) printk(KERN_WARNING "XENBUS unexpected type [%d], expected [%d]\n", msg.type, type); kfree(ret); return ERR_PTR(-EINVAL); } return ret;}/* Simplified version of xs_talkv: single message. */static void *xs_single(struct xenbus_transaction t, enum xsd_sockmsg_type type, const char *string, unsigned int *len){ struct kvec iovec; iovec.iov_base = (void *)string; iovec.iov_len = strlen(string) + 1; return xs_talkv(t, type, &iovec, 1, len);}/* Many commands only need an ack, don't care what it says. */static int xs_error(char *reply){ if (IS_ERR(reply)) return PTR_ERR(reply); kfree(reply); return 0;}static unsigned int count_strings(const char *strings, unsigned int len){ unsigned int num; const char *p; for (p = strings, num = 0; p < strings + len; p += strlen(p) + 1) num++; return num;}/* Return the path to dir with /name appended. Buffer must be kfree()'ed. */static char *join(const char *dir, const char *name){ char *buffer; if (strlen(name) == 0) buffer = kasprintf(GFP_KERNEL, "%s", dir); else buffer = kasprintf(GFP_KERNEL, "%s/%s", dir, name); return (!buffer) ? ERR_PTR(-ENOMEM) : buffer;}static char **split(char *strings, unsigned int len, unsigned int *num){ char *p, **ret; /* Count the strings. */ *num = count_strings(strings, len); /* Transfer to one big alloc for easy freeing. */ ret = kmalloc(*num * sizeof(char *) + len, GFP_KERNEL); if (!ret) { kfree(strings); return ERR_PTR(-ENOMEM); } memcpy(&ret[*num], strings, len); kfree(strings); strings = (char *)&ret[*num]; for (p = strings, *num = 0; p < strings + len; p += strlen(p) + 1) ret[(*num)++] = p; return ret;}char **xenbus_directory(struct xenbus_transaction t, const char *dir, const char *node, unsigned int *num){ char *strings, *path; unsigned int len; path = join(dir, node); if (IS_ERR(path)) return (char **)path; strings = xs_single(t, XS_DIRECTORY, path, &len); kfree(path); if (IS_ERR(strings)) return (char **)strings; return split(strings, len, num);}EXPORT_SYMBOL_GPL(xenbus_directory);/* Check if a path exists. Return 1 if it does. */int xenbus_exists(struct xenbus_transaction t, const char *dir, const char *node){ char **d; int dir_n; d = xenbus_directory(t, dir, node, &dir_n); if (IS_ERR(d)) return 0; kfree(d); return 1;}EXPORT_SYMBOL_GPL(xenbus_exists);/* Get the value of a single file. * Returns a kmalloced value: call free() on it after use. * len indicates length in bytes. */void *xenbus_read(struct xenbus_transaction t, const char *dir, const char *node, unsigned int *len){ char *path; void *ret; path = join(dir, node); if (IS_ERR(path)) return (void *)path; ret = xs_single(t, XS_READ, path, len); kfree(path); return ret;}EXPORT_SYMBOL_GPL(xenbus_read);/* Write the value of a single file. * Returns -err on failure. */int xenbus_write(struct xenbus_transaction t, const char *dir, const char *node, const char *string){ const char *path; struct kvec iovec[2]; int ret; path = join(dir, node); if (IS_ERR(path)) return PTR_ERR(path); iovec[0].iov_base = (void *)path; iovec[0].iov_len = strlen(path) + 1; iovec[1].iov_base = (void *)string; iovec[1].iov_len = strlen(string); ret = xs_error(xs_talkv(t, XS_WRITE, iovec, ARRAY_SIZE(iovec), NULL)); kfree(path); return ret;}EXPORT_SYMBOL_GPL(xenbus_write);/* Create a new directory. */int xenbus_mkdir(struct xenbus_transaction t, const char *dir, const char *node){ char *path; int ret; path = join(dir, node); if (IS_ERR(path)) return PTR_ERR(path); ret = xs_error(xs_single(t, XS_MKDIR, path, NULL)); kfree(path); return ret;}EXPORT_SYMBOL_GPL(xenbus_mkdir);/* Destroy a file or directory (directories must be empty). */int xenbus_rm(struct xenbus_transaction t, const char *dir, const char *node){ char *path; int ret; path = join(dir, node); if (IS_ERR(path)) return PTR_ERR(path); ret = xs_error(xs_single(t, XS_RM, path, NULL)); kfree(path); return ret;}EXPORT_SYMBOL_GPL(xenbus_rm);/* Start a transaction: changes by others will not be seen during this * transaction, and changes will not be visible to others until end. */int xenbus_transaction_start(struct xenbus_transaction *t){
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?