xref: /onnv-gate/usr/src/uts/common/xen/io/xenbus_client.c (revision 5741:58423876d513)
15084Sjohnlev /*
25084Sjohnlev  * CDDL HEADER START
35084Sjohnlev  *
45084Sjohnlev  * The contents of this file are subject to the terms of the
55084Sjohnlev  * Common Development and Distribution License (the "License").
65084Sjohnlev  * You may not use this file except in compliance with the License.
75084Sjohnlev  *
85084Sjohnlev  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95084Sjohnlev  * or http://www.opensolaris.org/os/licensing.
105084Sjohnlev  * See the License for the specific language governing permissions
115084Sjohnlev  * and limitations under the License.
125084Sjohnlev  *
135084Sjohnlev  * When distributing Covered Code, include this CDDL HEADER in each
145084Sjohnlev  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155084Sjohnlev  * If applicable, add the following below this CDDL HEADER, with the
165084Sjohnlev  * fields enclosed by brackets "[]" replaced with your own identifying
175084Sjohnlev  * information: Portions Copyright [yyyy] [name of copyright owner]
185084Sjohnlev  *
195084Sjohnlev  * CDDL HEADER END
205084Sjohnlev  */
215084Sjohnlev 
225084Sjohnlev /*
235084Sjohnlev  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
245084Sjohnlev  * Use is subject to license terms.
255084Sjohnlev  */
265084Sjohnlev 
275084Sjohnlev /*
285084Sjohnlev  * Client-facing interface for the Xenbus driver.  In other words, the
295084Sjohnlev  * interface between the Xenbus and the device-specific code, be it the
305084Sjohnlev  * frontend or the backend of that driver.
315084Sjohnlev  *
325084Sjohnlev  * Copyright (C) 2005 XenSource Ltd
335084Sjohnlev  *
345084Sjohnlev  * This file may be distributed separately from the Linux kernel, or
355084Sjohnlev  * incorporated into other software packages, subject to the following license:
365084Sjohnlev  *
375084Sjohnlev  * Permission is hereby granted, free of charge, to any person obtaining a copy
385084Sjohnlev  * of this source file (the "Software"), to deal in the Software without
395084Sjohnlev  * restriction, including without limitation the rights to use, copy, modify,
405084Sjohnlev  * merge, publish, distribute, sublicense, and/or sell copies of the Software,
415084Sjohnlev  * and to permit persons to whom the Software is furnished to do so, subject to
425084Sjohnlev  * the following conditions:
435084Sjohnlev  *
445084Sjohnlev  * The above copyright notice and this permission notice shall be included in
455084Sjohnlev  * all copies or substantial portions of the Software.
465084Sjohnlev  *
475084Sjohnlev  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
485084Sjohnlev  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
495084Sjohnlev  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
505084Sjohnlev  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
515084Sjohnlev  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
525084Sjohnlev  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
535084Sjohnlev  * IN THE SOFTWARE.
545084Sjohnlev  */
555084Sjohnlev 
565084Sjohnlev #pragma ident	"%Z%%M%	%I%	%E% SMI"
575084Sjohnlev 
58*5741Smrj #ifdef XPV_HVM_DRIVER
59*5741Smrj #include <sys/xpv_support.h>
60*5741Smrj #include <sys/hypervisor.h>
61*5741Smrj #else
625084Sjohnlev #include <sys/hypervisor.h>
635084Sjohnlev #include <sys/xen_mmu.h>
645084Sjohnlev #include <sys/evtchn_impl.h>
65*5741Smrj #endif
665084Sjohnlev #include <sys/gnttab.h>
675084Sjohnlev #include <xen/sys/xenbus_impl.h>
685084Sjohnlev #include <sys/cmn_err.h>
695084Sjohnlev 
705084Sjohnlev 
715084Sjohnlev int
xenbus_watch_path(struct xenbus_device * dev,const char * path,struct xenbus_watch * watch,void (* callback)(struct xenbus_watch *,const char **,unsigned int))725084Sjohnlev xenbus_watch_path(struct xenbus_device *dev, const char *path,
735084Sjohnlev 			struct xenbus_watch *watch,
745084Sjohnlev 			void (*callback)(struct xenbus_watch *,
755084Sjohnlev 			const char **, unsigned int))
765084Sjohnlev {
775084Sjohnlev 	int err;
785084Sjohnlev 
795084Sjohnlev 	watch->node = path;
805084Sjohnlev 	watch->callback = callback;
815084Sjohnlev 
825084Sjohnlev 	err = register_xenbus_watch(watch);
835084Sjohnlev 
845084Sjohnlev 	if (err) {
855084Sjohnlev 		watch->node = NULL;
865084Sjohnlev 		watch->callback = NULL;
875084Sjohnlev 		xenbus_dev_fatal(dev, err, "adding watch on %s", path);
885084Sjohnlev 	}
895084Sjohnlev 
905084Sjohnlev 	return (err);
915084Sjohnlev }
925084Sjohnlev 
935084Sjohnlev 
945084Sjohnlev int
xenbus_watch_path2(struct xenbus_device * dev,const char * path,const char * path2,struct xenbus_watch * watch,void (* callback)(struct xenbus_watch *,const char **,unsigned int))955084Sjohnlev xenbus_watch_path2(struct xenbus_device *dev, const char *path,
965084Sjohnlev 			const char *path2, struct xenbus_watch *watch,
975084Sjohnlev 			void (*callback)(struct xenbus_watch *,
985084Sjohnlev 			const char **, unsigned int))
995084Sjohnlev {
1005084Sjohnlev 	int err;
1015084Sjohnlev 	char *state;
1025084Sjohnlev 
1035084Sjohnlev 	state = kmem_alloc(strlen(path) + 1 + strlen(path2) + 1, KM_SLEEP);
1045084Sjohnlev 	(void) strcpy(state, path);
1055084Sjohnlev 	(void) strcat(state, "/");
1065084Sjohnlev 	(void) strcat(state, path2);
1075084Sjohnlev 
1085084Sjohnlev 	err = xenbus_watch_path(dev, state, watch, callback);
1095084Sjohnlev 	if (err)
1105084Sjohnlev 		kmem_free(state, strlen(state) + 1);
1115084Sjohnlev 	return (err);
1125084Sjohnlev }
1135084Sjohnlev 
1145084Sjohnlev /*
1155084Sjohnlev  * Returns 0 on success, -1 if no change was made, or an errno on failure.  We
1165084Sjohnlev  * check whether the state is currently set to the given value, and if not,
1175084Sjohnlev  * then the state is set.  We don't want to unconditionally write the given
1185084Sjohnlev  * state, because we don't want to fire watches unnecessarily.  Furthermore, if
1195084Sjohnlev  * the node has gone, we don't write to it, as the device will be tearing down,
1205084Sjohnlev  * and we don't want to resurrect that directory.
1215084Sjohnlev  *
1225084Sjohnlev  * XXPV: not clear that this is still safe if two threads are racing to update
1235084Sjohnlev  * the state?
1245084Sjohnlev  */
1255084Sjohnlev int
xenbus_switch_state(struct xenbus_device * dev,xenbus_transaction_t xbt,XenbusState state)1265084Sjohnlev xenbus_switch_state(struct xenbus_device *dev, xenbus_transaction_t xbt,
1275084Sjohnlev 			XenbusState state)
1285084Sjohnlev {
1295084Sjohnlev 	int current_state;
1305084Sjohnlev 	int err;
1315084Sjohnlev 
1325084Sjohnlev 	err = xenbus_scanf(xbt, dev->nodename, "state", "%d", &current_state);
1335084Sjohnlev 
1345084Sjohnlev 	/* XXPV: is this really the right thing to do? */
1355084Sjohnlev 	if (err == ENOENT)
1365084Sjohnlev 		return (0);
1375084Sjohnlev 	if (err)
1385084Sjohnlev 		return (err);
1395084Sjohnlev 
1405084Sjohnlev 	err = -1;
1415084Sjohnlev 
1425084Sjohnlev 	if ((XenbusState)current_state != state) {
1435084Sjohnlev 		err = xenbus_printf(xbt, dev->nodename, "state", "%d", state);
1445084Sjohnlev 		if (err)
1455084Sjohnlev 			xenbus_dev_fatal(dev, err, "writing new state");
1465084Sjohnlev 	}
1475084Sjohnlev 
1485084Sjohnlev 	return (err);
1495084Sjohnlev }
1505084Sjohnlev 
1515084Sjohnlev 
1525084Sjohnlev /*
1535084Sjohnlev  * Return the path to the error node for the given device, or NULL on failure.
1545084Sjohnlev  * If the value returned is non-NULL, then it is the caller's to kmem_free.
1555084Sjohnlev  */
1565084Sjohnlev static char *
error_path(struct xenbus_device * dev)1575084Sjohnlev error_path(struct xenbus_device *dev)
1585084Sjohnlev {
1595084Sjohnlev 	char *path_buffer;
1605084Sjohnlev 
1615084Sjohnlev 	path_buffer = kmem_alloc(strlen("error/") + strlen(dev->nodename) +
1625084Sjohnlev 	    1, KM_SLEEP);
1635084Sjohnlev 
1645084Sjohnlev 	(void) strcpy(path_buffer, "error/");
1655084Sjohnlev 	(void) strcpy(path_buffer + strlen("error/"), dev->nodename);
1665084Sjohnlev 
1675084Sjohnlev 	return (path_buffer);
1685084Sjohnlev }
1695084Sjohnlev 
1705084Sjohnlev static void
common_dev_error(struct xenbus_device * dev,int err,const char * fmt,va_list ap)1715084Sjohnlev common_dev_error(struct xenbus_device *dev, int err, const char *fmt,
1725084Sjohnlev 		va_list ap)
1735084Sjohnlev {
1745084Sjohnlev 	int ret;
1755084Sjohnlev 	unsigned int len;
1765084Sjohnlev 	char *printf_buffer = NULL, *path_buffer = NULL;
1775084Sjohnlev 
1785084Sjohnlev #define	PRINTF_BUFFER_SIZE 4096
1795084Sjohnlev 	printf_buffer = kmem_alloc(PRINTF_BUFFER_SIZE, KM_SLEEP);
1805084Sjohnlev 
1815084Sjohnlev 	(void) snprintf(printf_buffer, PRINTF_BUFFER_SIZE, "%d ", err);
1825084Sjohnlev 	len = strlen(printf_buffer);
1835084Sjohnlev 	ret = vsnprintf(printf_buffer+len, PRINTF_BUFFER_SIZE-len, fmt, ap);
1845084Sjohnlev 
1855084Sjohnlev 	ASSERT(len + ret <= PRINTF_BUFFER_SIZE-1);
1865084Sjohnlev 	dev->has_error = 1;
1875084Sjohnlev 
1885084Sjohnlev 	path_buffer = error_path(dev);
1895084Sjohnlev 
1905084Sjohnlev 	if (path_buffer == NULL) {
1915084Sjohnlev 		printf("xenbus: failed to write error node for %s (%s)\n",
1925084Sjohnlev 		    dev->nodename, printf_buffer);
1935084Sjohnlev 		goto fail;
1945084Sjohnlev 	}
1955084Sjohnlev 
1965084Sjohnlev 	if (xenbus_write(NULL, path_buffer, "error", printf_buffer) != 0) {
1975084Sjohnlev 		printf("xenbus: failed to write error node for %s (%s)\n",
1985084Sjohnlev 		    dev->nodename, printf_buffer);
1995084Sjohnlev 		goto fail;
2005084Sjohnlev 	}
2015084Sjohnlev 
2025084Sjohnlev fail:
2035084Sjohnlev 	if (printf_buffer)
2045084Sjohnlev 		kmem_free(printf_buffer, PRINTF_BUFFER_SIZE);
2055084Sjohnlev 	if (path_buffer)
2065084Sjohnlev 		kmem_free(path_buffer, strlen(path_buffer) + 1);
2075084Sjohnlev }
2085084Sjohnlev 
2095084Sjohnlev 
2105084Sjohnlev void
xenbus_dev_error(struct xenbus_device * dev,int err,const char * fmt,...)2115084Sjohnlev xenbus_dev_error(struct xenbus_device *dev, int err, const char *fmt, ...)
2125084Sjohnlev {
2135084Sjohnlev 	va_list ap;
2145084Sjohnlev 
2155084Sjohnlev 	va_start(ap, fmt);
2165084Sjohnlev 	common_dev_error(dev, err, fmt, ap);
2175084Sjohnlev 	va_end(ap);
2185084Sjohnlev }
2195084Sjohnlev 
2205084Sjohnlev 
2215084Sjohnlev void
xenbus_dev_fatal(struct xenbus_device * dev,int err,const char * fmt,...)2225084Sjohnlev xenbus_dev_fatal(struct xenbus_device *dev, int err, const char *fmt, ...)
2235084Sjohnlev {
2245084Sjohnlev 	va_list ap;
2255084Sjohnlev 
2265084Sjohnlev 	va_start(ap, fmt);
2275084Sjohnlev 	common_dev_error(dev, err, fmt, ap);
2285084Sjohnlev 	va_end(ap);
2295084Sjohnlev 
2305084Sjohnlev 	(void) xenbus_switch_state(dev, XBT_NULL, XenbusStateClosing);
2315084Sjohnlev }
2325084Sjohnlev 
2335084Sjohnlev /* Clear any error. */
2345084Sjohnlev void
xenbus_dev_ok(struct xenbus_device * dev)2355084Sjohnlev xenbus_dev_ok(struct xenbus_device *dev)
2365084Sjohnlev {
2375084Sjohnlev 	if (dev->has_error) {
2385084Sjohnlev 		if (xenbus_rm(NULL, dev->nodename, "error") != 0)
2395084Sjohnlev 			printf("xenbus: failed to clear error node for %s\n",
2405084Sjohnlev 			    dev->nodename);
2415084Sjohnlev 		else
2425084Sjohnlev 			dev->has_error = 0;
2435084Sjohnlev 	}
2445084Sjohnlev }
2455084Sjohnlev 
2465084Sjohnlev int
xenbus_grant_ring(struct xenbus_device * dev,unsigned long ring_mfn)2475084Sjohnlev xenbus_grant_ring(struct xenbus_device *dev, unsigned long ring_mfn)
2485084Sjohnlev {
2495084Sjohnlev 	int err = gnttab_grant_foreign_access(dev->otherend_id, ring_mfn, 0);
2505084Sjohnlev 	if (err < 0)
2515084Sjohnlev 		xenbus_dev_fatal(dev, err, "granting access to ring page");
2525084Sjohnlev 	return (err);
2535084Sjohnlev }
2545084Sjohnlev 
2555084Sjohnlev 
2565084Sjohnlev int
xenbus_alloc_evtchn(struct xenbus_device * dev,int * port)2575084Sjohnlev xenbus_alloc_evtchn(struct xenbus_device *dev, int *port)
2585084Sjohnlev {
2595084Sjohnlev 	int err;
2605084Sjohnlev 
2615084Sjohnlev 	err = xen_alloc_unbound_evtchn(dev->otherend_id, port);
2625084Sjohnlev 	if (err)
2635084Sjohnlev 		xenbus_dev_fatal(dev, err, "allocating event channel");
2645084Sjohnlev 	return (err);
2655084Sjohnlev }
2665084Sjohnlev 
2675084Sjohnlev 
2685084Sjohnlev XenbusState
xenbus_read_driver_state(const char * path)2695084Sjohnlev xenbus_read_driver_state(const char *path)
2705084Sjohnlev {
2715084Sjohnlev 	XenbusState result;
2725084Sjohnlev 
2735084Sjohnlev 	int err = xenbus_gather(XBT_NULL, path, "state", "%d", &result, NULL);
2745084Sjohnlev 	if (err)
2755084Sjohnlev 		result = XenbusStateClosed;
2765084Sjohnlev 
2775084Sjohnlev 	return (result);
2785084Sjohnlev }
2795084Sjohnlev 
2805084Sjohnlev 
2815084Sjohnlev /*
2825084Sjohnlev  * Local variables:
2835084Sjohnlev  *  c-file-style: "solaris"
2845084Sjohnlev  *  indent-tabs-mode: t
2855084Sjohnlev  *  c-indent-level: 8
2865084Sjohnlev  *  c-basic-offset: 8
2875084Sjohnlev  *  tab-width: 8
2885084Sjohnlev  * End:
2895084Sjohnlev  */
290