diff options
Diffstat (limited to 'drivers/xen/xenbus/xenbus_xs.c')
| -rw-r--r-- | drivers/xen/xenbus/xenbus_xs.c | 134 | 
1 files changed, 107 insertions, 27 deletions
diff --git a/drivers/xen/xenbus/xenbus_xs.c b/drivers/xen/xenbus/xenbus_xs.c index 5534690075a..ba804f3d827 100644 --- a/drivers/xen/xenbus/xenbus_xs.c +++ b/drivers/xen/xenbus/xenbus_xs.c @@ -31,6 +31,8 @@   * IN THE SOFTWARE.   */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +  #include <linux/unistd.h>  #include <linux/errno.h>  #include <linux/types.h> @@ -44,8 +46,11 @@  #include <linux/rwsem.h>  #include <linux/module.h>  #include <linux/mutex.h> +#include <asm/xen/hypervisor.h>  #include <xen/xenbus.h> +#include <xen/xen.h>  #include "xenbus_comms.h" +#include "xenbus_probe.h"  struct xs_stored_msg {  	struct list_head list; @@ -127,15 +132,37 @@ static int get_error(const char *errorstring)  	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); +			pr_warn("xen store gave: unknown error %s\n", +				errorstring);  			return EINVAL;  		}  	}  	return xsd_errors[i].errnum;  } +static bool xenbus_ok(void) +{ +	switch (xen_store_domain_type) { +	case XS_LOCAL: +		switch (system_state) { +		case SYSTEM_POWER_OFF: +		case SYSTEM_RESTART: +		case SYSTEM_HALT: +			return false; +		default: +			break; +		} +		return true; +	case XS_PV: +	case XS_HVM: +		/* FIXME: Could check that the remote domain is alive, +		 * but it is normally initial domain. */ +		return true; +	default: +		break; +	} +	return false; +}  static void *read_reply(enum xsd_sockmsg_type *type, unsigned int *len)  {  	struct xs_stored_msg *msg; @@ -145,9 +172,20 @@ static void *read_reply(enum xsd_sockmsg_type *type, unsigned int *len)  	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)); +		if (xenbus_ok()) +			/* XXX FIXME: Avoid synchronous wait for response here. */ +			wait_event_timeout(xs_state.reply_waitq, +					   !list_empty(&xs_state.reply_list), +					   msecs_to_jiffies(500)); +		else { +			/* +			 * If we are in the process of being shut-down there is +			 * no point of trying to contact XenBus - it is either +			 * killed (xenstored application) or the other domain +			 * has been killed or is unreachable. +			 */ +			return ERR_PTR(-EIO); +		}  		spin_lock(&xs_state.reply_lock);  	} @@ -212,6 +250,9 @@ void *xenbus_dev_request_and_reply(struct xsd_sockmsg *msg)  	mutex_unlock(&xs_state.request_mutex); +	if (IS_ERR(ret)) +		return ret; +  	if ((msg->type == XS_TRANSACTION_END) ||  	    ((req_msg.type == XS_TRANSACTION_START) &&  	     (msg->type == XS_ERROR))) @@ -270,10 +311,8 @@ static void *xs_talkv(struct xenbus_transaction t,  	}  	if (msg.type != type) { -		if (printk_ratelimit()) -			printk(KERN_WARNING -			       "XENBUS unexpected type [%d], expected [%d]\n", -			       msg.type, type); +		pr_warn_ratelimited("unexpected type [%d], expected [%d]\n", +				    msg.type, type);  		kfree(ret);  		return ERR_PTR(-EINVAL);  	} @@ -531,21 +570,18 @@ int xenbus_printf(struct xenbus_transaction t,  {  	va_list ap;  	int ret; -#define PRINTF_BUFFER_SIZE 4096 -	char *printf_buffer; - -	printf_buffer = kmalloc(PRINTF_BUFFER_SIZE, GFP_NOIO | __GFP_HIGH); -	if (printf_buffer == NULL) -		return -ENOMEM; +	char *buf;  	va_start(ap, fmt); -	ret = vsnprintf(printf_buffer, PRINTF_BUFFER_SIZE, fmt, ap); +	buf = kvasprintf(GFP_NOIO | __GFP_HIGH, fmt, ap);  	va_end(ap); -	BUG_ON(ret > PRINTF_BUFFER_SIZE-1); -	ret = xenbus_write(t, dir, node, printf_buffer); +	if (!buf) +		return -ENOMEM; + +	ret = xenbus_write(t, dir, node, buf); -	kfree(printf_buffer); +	kfree(buf);  	return ret;  } @@ -619,6 +655,45 @@ static struct xenbus_watch *find_watch(const char *token)  	return NULL;  } +/* + * Certain older XenBus toolstack cannot handle reading values that are + * not populated. Some Xen 3.4 installation are incapable of doing this + * so if we are running on anything older than 4 do not attempt to read + * control/platform-feature-xs_reset_watches. + */ +static bool xen_strict_xenbus_quirk(void) +{ +#ifdef CONFIG_X86 +	uint32_t eax, ebx, ecx, edx, base; + +	base = xen_cpuid_base(); +	cpuid(base + 1, &eax, &ebx, &ecx, &edx); + +	if ((eax >> 16) < 4) +		return true; +#endif +	return false; + +} +static void xs_reset_watches(void) +{ +	int err, supported = 0; + +	if (!xen_hvm_domain() || xen_initial_domain()) +		return; + +	if (xen_strict_xenbus_quirk()) +		return; + +	err = xenbus_scanf(XBT_NIL, "control", +			"platform-feature-xs_reset_watches", "%d", &supported); +	if (err != 1 || !supported) +		return; + +	err = xs_error(xs_single(XBT_NIL, XS_RESET_WATCHES, "", NULL)); +	if (err && err != -EEXIST) +		pr_warn("xs_reset_watches failed: %d\n", err); +}  /* Register callback to watch this node. */  int register_xenbus_watch(struct xenbus_watch *watch) @@ -638,8 +713,7 @@ int register_xenbus_watch(struct xenbus_watch *watch)  	err = xs_watch(watch->node, token); -	/* Ignore errors due to multiple registration. */ -	if ((err != 0) && (err != -EEXIST)) { +	if (err) {  		spin_lock(&watches_lock);  		list_del(&watch->list);  		spin_unlock(&watches_lock); @@ -668,9 +742,7 @@ void unregister_xenbus_watch(struct xenbus_watch *watch)  	err = xs_unwatch(watch->node, token);  	if (err) -		printk(KERN_WARNING -		       "XENBUS Failed to release watch %s: %i\n", -		       watch->node, err); +		pr_warn("Failed to release watch %s: %i\n", watch->node, err);  	up_read(&xs_state.watch_mutex); @@ -801,6 +873,12 @@ static int process_msg(void)  		goto out;  	} +	if (msg->hdr.len > XENSTORE_PAYLOAD_MAX) { +		kfree(msg); +		err = -EINVAL; +		goto out; +	} +  	body = kmalloc(msg->hdr.len + 1, GFP_NOIO | __GFP_HIGH);  	if (body == NULL) {  		kfree(msg); @@ -858,8 +936,7 @@ static int xenbus_thread(void *unused)  	for (;;) {  		err = process_msg();  		if (err) -			printk(KERN_WARNING "XENBUS error %d while reading " -			       "message\n", err); +			pr_warn("error %d while reading message\n", err);  		if (kthread_should_stop())  			break;  	} @@ -897,5 +974,8 @@ int xs_init(void)  	if (IS_ERR(task))  		return PTR_ERR(task); +	/* shutdown watches for kexec boot */ +	xs_reset_watches(); +  	return 0;  }  | 
