Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f28169d2 authored by Jeff Dike's avatar Jeff Dike Committed by Linus Torvalds
Browse files

[PATCH] uml: return hotplug errors to host



I noticed that errors happening while hotplugging devices from the host were
never returned back to the mconsole client.  In some cases, success was
returned instead of even an information-free error.

This patch cleans that up by having the low-level configuration code pass back
an error string along with an error code.  At the top level, which knows
whether it is early boot time or responding to an mconsole request, the string
is printk'd or returned to the mconsole client.

There are also whitespace and trivial code cleanups in the surrounding code.

Signed-off-by: default avatarJeff Dike <jdike@addtoit.com>
Cc: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent d79a5809
Loading
Loading
Loading
Loading
+23 −54
Original line number Original line Diff line number Diff line
@@ -19,44 +19,11 @@
#include "line.h"
#include "line.h"
#include "os.h"
#include "os.h"


/* XXX: could well be moved to somewhere else, if needed. */
static int my_printf(const char * fmt, ...)
	__attribute__ ((format (printf, 1, 2)));

static int my_printf(const char * fmt, ...)
{
	/* Yes, can be called on atomic context.*/
	char *buf = kmalloc(4096, GFP_ATOMIC);
	va_list args;
	int r;

	if (!buf) {
		/* We print directly fmt.
		 * Yes, yes, yes, feel free to complain. */
		r = strlen(fmt);
	} else {
		va_start(args, fmt);
		r = vsprintf(buf, fmt, args);
		va_end(args);
		fmt = buf;
	}

	if (r)
		r = os_write_file(1, fmt, r);
	return r;

}

#ifdef CONFIG_NOCONFIG_CHAN
#ifdef CONFIG_NOCONFIG_CHAN
/* Despite its name, there's no added trailing newline. */
static void *not_configged_init(char *str, int device,
static int my_puts(const char * buf)
				const struct chan_opts *opts)
{
	return os_write_file(1, buf, strlen(buf));
}

static void *not_configged_init(char *str, int device, struct chan_opts *opts)
{
{
	my_puts("Using a channel type which is configured out of "
	printk("Using a channel type which is configured out of "
	       "UML\n");
	       "UML\n");
	return NULL;
	return NULL;
}
}
@@ -64,34 +31,34 @@ static void *not_configged_init(char *str, int device, struct chan_opts *opts)
static int not_configged_open(int input, int output, int primary, void *data,
static int not_configged_open(int input, int output, int primary, void *data,
			      char **dev_out)
			      char **dev_out)
{
{
	my_puts("Using a channel type which is configured out of "
	printk("Using a channel type which is configured out of "
	       "UML\n");
	       "UML\n");
	return -ENODEV;
	return -ENODEV;
}
}


static void not_configged_close(int fd, void *data)
static void not_configged_close(int fd, void *data)
{
{
	my_puts("Using a channel type which is configured out of "
	printk("Using a channel type which is configured out of "
	       "UML\n");
	       "UML\n");
}
}


static int not_configged_read(int fd, char *c_out, void *data)
static int not_configged_read(int fd, char *c_out, void *data)
{
{
	my_puts("Using a channel type which is configured out of "
	printk("Using a channel type which is configured out of "
	       "UML\n");
	       "UML\n");
	return -EIO;
	return -EIO;
}
}


static int not_configged_write(int fd, const char *buf, int len, void *data)
static int not_configged_write(int fd, const char *buf, int len, void *data)
{
{
	my_puts("Using a channel type which is configured out of "
	printk("Using a channel type which is configured out of "
	       "UML\n");
	       "UML\n");
	return -EIO;
	return -EIO;
}
}


static int not_configged_console_write(int fd, const char *buf, int len)
static int not_configged_console_write(int fd, const char *buf, int len)
{
{
	my_puts("Using a channel type which is configured out of "
	printk("Using a channel type which is configured out of "
	       "UML\n");
	       "UML\n");
	return -EIO;
	return -EIO;
}
}
@@ -99,14 +66,14 @@ static int not_configged_console_write(int fd, const char *buf, int len)
static int not_configged_window_size(int fd, void *data, unsigned short *rows,
static int not_configged_window_size(int fd, void *data, unsigned short *rows,
				     unsigned short *cols)
				     unsigned short *cols)
{
{
	my_puts("Using a channel type which is configured out of "
	printk("Using a channel type which is configured out of "
	       "UML\n");
	       "UML\n");
	return -ENODEV;
	return -ENODEV;
}
}


static void not_configged_free(void *data)
static void not_configged_free(void *data)
{
{
	my_puts("Using a channel type which is configured out of "
	printk("Using a channel type which is configured out of "
	       "UML\n");
	       "UML\n");
}
}


@@ -534,7 +501,7 @@ static const struct chan_type chan_table[] = {
};
};


static struct chan *parse_chan(struct line *line, char *str, int device,
static struct chan *parse_chan(struct line *line, char *str, int device,
			       const struct chan_opts *opts)
			       const struct chan_opts *opts, char **error_out)
{
{
	const struct chan_type *entry;
	const struct chan_type *entry;
	const struct chan_ops *ops;
	const struct chan_ops *ops;
@@ -553,19 +520,21 @@ static struct chan *parse_chan(struct line *line, char *str, int device,
		}
		}
	}
	}
	if(ops == NULL){
	if(ops == NULL){
		my_printf("parse_chan couldn't parse \"%s\"\n",
		*error_out = "No match for configured backends";
		       str);
		return NULL;
		return NULL;
	}
	}
	if(ops->init == NULL)

		return NULL;
	data = (*ops->init)(str, device, opts);
	data = (*ops->init)(str, device, opts);
	if(data == NULL)
	if(data == NULL){
		*error_out = "Configuration failed";
		return NULL;
		return NULL;
	}


	chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
	chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
	if(chan == NULL)
	if(chan == NULL){
		*error_out = "Memory allocation failed";
		return NULL;
		return NULL;
	}
	*chan = ((struct chan) { .list	 	= LIST_HEAD_INIT(chan->list),
	*chan = ((struct chan) { .list	 	= LIST_HEAD_INIT(chan->list),
				 .free_list 	=
				 .free_list 	=
				 	LIST_HEAD_INIT(chan->free_list),
				 	LIST_HEAD_INIT(chan->free_list),
@@ -582,7 +551,7 @@ static struct chan *parse_chan(struct line *line, char *str, int device,
}
}


int parse_chan_pair(char *str, struct line *line, int device,
int parse_chan_pair(char *str, struct line *line, int device,
		    const struct chan_opts *opts)
		    const struct chan_opts *opts, char **error_out)
{
{
	struct list_head *chans = &line->chan_list;
	struct list_head *chans = &line->chan_list;
	struct chan *new, *chan;
	struct chan *new, *chan;
@@ -599,14 +568,14 @@ int parse_chan_pair(char *str, struct line *line, int device,
		in = str;
		in = str;
		*out = '\0';
		*out = '\0';
		out++;
		out++;
		new = parse_chan(line, in, device, opts);
		new = parse_chan(line, in, device, opts, error_out);
		if(new == NULL)
		if(new == NULL)
			return -1;
			return -1;


		new->input = 1;
		new->input = 1;
		list_add(&new->list, chans);
		list_add(&new->list, chans);


		new = parse_chan(line, out, device, opts);
		new = parse_chan(line, out, device, opts, error_out);
		if(new == NULL)
		if(new == NULL)
			return -1;
			return -1;


@@ -614,7 +583,7 @@ int parse_chan_pair(char *str, struct line *line, int device,
		new->output = 1;
		new->output = 1;
	}
	}
	else {
	else {
		new = parse_chan(line, str, device, opts);
		new = parse_chan(line, str, device, opts, error_out);
		if(new == NULL)
		if(new == NULL)
			return -1;
			return -1;


+39 −27
Original line number Original line Diff line number Diff line
@@ -549,14 +549,16 @@ void close_lines(struct line *lines, int nlines)
		close_chan(&lines[i].chan_list, 0);
		close_chan(&lines[i].chan_list, 0);
}
}


static void setup_one_line(struct line *lines, int n, char *init, int init_prio)
static int setup_one_line(struct line *lines, int n, char *init, int init_prio,
			  char **error_out)
{
{
	struct line *line = &lines[n];
	struct line *line = &lines[n];
	int err = -EINVAL;


	spin_lock(&line->count_lock);
	spin_lock(&line->count_lock);


	if(line->tty != NULL){
	if(line->tty != NULL){
		printk("line_setup - device %d is open\n", n);
		*error_out = "Device is already open";
		goto out;
		goto out;
	}
	}


@@ -569,18 +571,22 @@ static void setup_one_line(struct line *lines, int n, char *init, int init_prio)
			line->valid = 1;
			line->valid = 1;
		}
		}
	}
	}
	err = 0;
out:
out:
	spin_unlock(&line->count_lock);
	spin_unlock(&line->count_lock);
	return err;
}
}


/* Common setup code for both startup command line and mconsole initialization.
/* Common setup code for both startup command line and mconsole initialization.
 * @lines contains the array (of size @num) to modify;
 * @lines contains the array (of size @num) to modify;
 * @init is the setup string;
 * @init is the setup string;
 * @error_out is an error string in the case of failure;
 */
 */


int line_setup(struct line *lines, unsigned int num, char *init)
int line_setup(struct line *lines, unsigned int num, char *init,
	       char **error_out)
{
{
	int i, n;
	int i, n, err;
	char *end;
	char *end;


	if(*init == '=') {
	if(*init == '=') {
@@ -591,52 +597,56 @@ int line_setup(struct line *lines, unsigned int num, char *init)
	else {
	else {
		n = simple_strtoul(init, &end, 0);
		n = simple_strtoul(init, &end, 0);
		if(*end != '='){
		if(*end != '='){
			printk(KERN_ERR "line_setup failed to parse \"%s\"\n",
			*error_out = "Couldn't parse device number";
			       init);
			return -EINVAL;
			return 0;
		}
		}
		init = end;
		init = end;
	}
	}
	init++;
	init++;


	if (n >= (signed int) num) {
	if (n >= (signed int) num) {
		printk("line_setup - %d out of range ((0 ... %d) allowed)\n",
		*error_out = "Device number out of range";
		       n, num - 1);
		return -EINVAL;
		return 0;
	}
	else if (n >= 0){
		err = setup_one_line(lines, n, init, INIT_ONE, error_out);
		if(err)
			return err;
	}
	}
	else if (n >= 0)
		setup_one_line(lines, n, init, INIT_ONE);
	else {
	else {
		for(i = 0; i < num; i++)
		for(i = 0; i < num; i++){
			setup_one_line(lines, i, init, INIT_ALL);
			err = setup_one_line(lines, i, init, INIT_ALL,
					     error_out);
			if(err)
				return err;
		}
	}
	}
	return n == -1 ? num : n;
	return n == -1 ? num : n;
}
}


int line_config(struct line *lines, unsigned int num, char *str,
int line_config(struct line *lines, unsigned int num, char *str,
		const struct chan_opts *opts)
		const struct chan_opts *opts, char **error_out)
{
{
	struct line *line;
	struct line *line;
	char *new;
	char *new;
	int n;
	int n;


	if(*str == '='){
	if(*str == '='){
		printk("line_config - can't configure all devices from "
		*error_out = "Can't configure all devices from mconsole";
		       "mconsole\n");
		return -EINVAL;
		return 1;
	}
	}


	new = kstrdup(str, GFP_KERNEL);
	new = kstrdup(str, GFP_KERNEL);
	if(new == NULL){
	if(new == NULL){
		printk("line_config - kstrdup failed\n");
		*error_out = "Failed to allocate memory";
		return 1;
		return -ENOMEM;
	}
	}
	n = line_setup(lines, num, new);
	n = line_setup(lines, num, new, error_out);
	if(n < 0)
	if(n < 0)
		return 1;
		return n;


	line = &lines[n];
	line = &lines[n];
	return parse_chan_pair(line->init_str, line, n, opts);
	return parse_chan_pair(line->init_str, line, n, opts, error_out);
}
}


int line_get_config(char *name, struct line *lines, unsigned int num, char *str,
int line_get_config(char *name, struct line *lines, unsigned int num, char *str,
@@ -685,13 +695,13 @@ int line_id(char **str, int *start_out, int *end_out)
        return n;
        return n;
}
}


int line_remove(struct line *lines, unsigned int num, int n)
int line_remove(struct line *lines, unsigned int num, int n, char **error_out)
{
{
	int err;
	int err;
	char config[sizeof("conxxxx=none\0")];
	char config[sizeof("conxxxx=none\0")];


	sprintf(config, "%d=none", n);
	sprintf(config, "%d=none", n);
	err = line_setup(lines, num, config);
	err = line_setup(lines, num, config, error_out);
	if(err >= 0)
	if(err >= 0)
		err = 0;
		err = 0;
	return err;
	return err;
@@ -740,6 +750,7 @@ static LIST_HEAD(winch_handlers);
void lines_init(struct line *lines, int nlines, struct chan_opts *opts)
void lines_init(struct line *lines, int nlines, struct chan_opts *opts)
{
{
	struct line *line;
	struct line *line;
	char *error;
	int i;
	int i;


	for(i = 0; i < nlines; i++){
	for(i = 0; i < nlines; i++){
@@ -754,8 +765,9 @@ void lines_init(struct line *lines, int nlines, struct chan_opts *opts)
		if(line->init_str == NULL)
		if(line->init_str == NULL)
			printk("lines_init - kstrdup returned NULL\n");
			printk("lines_init - kstrdup returned NULL\n");


		if(parse_chan_pair(line->init_str, line, i, opts)){
		if(parse_chan_pair(line->init_str, line, i, opts, &error)){
			printk("parse_chan_pair failed for device %d\n", i);
			printk("parse_chan_pair failed for device %d : %s\n",
			       i, error);
			line->valid = 0;
			line->valid = 0;
		}
		}
	}
	}
+27 −13
Original line number Original line Diff line number Diff line
@@ -371,14 +371,16 @@ static unsigned long long unplugged_pages_count = 0;
static struct list_head unplugged_pages = LIST_HEAD_INIT(unplugged_pages);
static struct list_head unplugged_pages = LIST_HEAD_INIT(unplugged_pages);
static int unplug_index = UNPLUGGED_PER_PAGE;
static int unplug_index = UNPLUGGED_PER_PAGE;


static int mem_config(char *str)
static int mem_config(char *str, char **error_out)
{
{
	unsigned long long diff;
	unsigned long long diff;
	int err = -EINVAL, i, add;
	int err = -EINVAL, i, add;
	char *ret;
	char *ret;


	if(str[0] != '=')
	if(str[0] != '='){
		*error_out = "Expected '=' after 'mem'";
		goto out;
		goto out;
	}


	str++;
	str++;
	if(str[0] == '-')
	if(str[0] == '-')
@@ -386,12 +388,17 @@ static int mem_config(char *str)
	else if(str[0] == '+'){
	else if(str[0] == '+'){
		add = 1;
		add = 1;
	}
	}
	else goto out;
	else {
		*error_out = "Expected increment to start with '-' or '+'";
		goto out;
	}


	str++;
	str++;
	diff = memparse(str, &ret);
	diff = memparse(str, &ret);
	if(*ret != '\0')
	if(*ret != '\0'){
		*error_out = "Failed to parse memory increment";
		goto out;
		goto out;
	}


	diff /= PAGE_SIZE;
	diff /= PAGE_SIZE;


@@ -435,11 +442,14 @@ static int mem_config(char *str)
				unplugged = list_entry(entry,
				unplugged = list_entry(entry,
						       struct unplugged_pages,
						       struct unplugged_pages,
						       list);
						       list);
				unplugged->pages[unplug_index++] = addr;
				err = os_drop_memory(addr, PAGE_SIZE);
				err = os_drop_memory(addr, PAGE_SIZE);
				if(err)
				if(err){
					printk("Failed to release memory - "
					printk("Failed to release memory - "
					       "errno = %d\n", err);
					       "errno = %d\n", err);
					*error_out = "Failed to release memory";
					goto out;
				}
				unplugged->pages[unplug_index++] = addr;
			}
			}


			unplugged_pages_count++;
			unplugged_pages_count++;
@@ -470,8 +480,9 @@ static int mem_id(char **str, int *start_out, int *end_out)
	return 0;
	return 0;
}
}


static int mem_remove(int n)
static int mem_remove(int n, char **error_out)
{
{
	*error_out = "Memory doesn't support the remove operation";
	return -EBUSY;
	return -EBUSY;
}
}


@@ -542,7 +553,7 @@ static void mconsole_get_config(int (*get_config)(char *, char *, int,
void mconsole_config(struct mc_request *req)
void mconsole_config(struct mc_request *req)
{
{
	struct mc_device *dev;
	struct mc_device *dev;
	char *ptr = req->request.data, *name;
	char *ptr = req->request.data, *name, *error_string = "";
	int err;
	int err;


	ptr += strlen("config");
	ptr += strlen("config");
@@ -559,8 +570,8 @@ void mconsole_config(struct mc_request *req)
		ptr++;
		ptr++;


	if(*ptr == '='){
	if(*ptr == '='){
		err = (*dev->config)(name);
		err = (*dev->config)(name, &error_string);
		mconsole_reply(req, "", err, 0);
		mconsole_reply(req, error_string, err, 0);
	}
	}
	else mconsole_get_config(dev->get_config, req, name);
	else mconsole_get_config(dev->get_config, req, name);
}
}
@@ -595,12 +606,15 @@ void mconsole_remove(struct mc_request *req)
		goto out;
		goto out;
	}
	}


	err = (*dev->remove)(n);
	err_msg = NULL;
	err = (*dev->remove)(n, &err_msg);
	switch(err){
	switch(err){
	case -ENODEV:
	case -ENODEV:
		if(err_msg == NULL)
			err_msg = "Device doesn't exist";
			err_msg = "Device doesn't exist";
		break;
		break;
	case -EBUSY:
	case -EBUSY:
		if(err_msg == NULL)
			err_msg = "Device is currently open";
			err_msg = "Device is currently open";
		break;
		break;
	default:
	default:
+46 −51
Original line number Original line Diff line number Diff line
@@ -463,35 +463,33 @@ static struct uml_net *find_device(int n)
	return(device);
	return(device);
}
}


static int eth_parse(char *str, int *index_out, char **str_out)
static int eth_parse(char *str, int *index_out, char **str_out,
		     char **error_out)
{
{
	char *end;
	char *end;
	int n;
	int n, err = -EINVAL;;


	n = simple_strtoul(str, &end, 0);
	n = simple_strtoul(str, &end, 0);
	if(end == str){
	if(end == str){
		printk(KERN_ERR "eth_setup: Failed to parse '%s'\n", str);
		*error_out = "Bad device number";
		return(1);
		return err;
	}
	if(n < 0){
		printk(KERN_ERR "eth_setup: device %d is negative\n", n);
		return(1);
	}
	}

	str = end;
	str = end;
	if(*str != '='){
	if(*str != '='){
		printk(KERN_ERR 
		*error_out = "Expected '=' after device number";
		       "eth_setup: expected '=' after device number\n");
		return err;
		return(1);
	}
	}

	str++;
	str++;
	if(find_device(n)){
	if(find_device(n)){
		printk(KERN_ERR "eth_setup: Device %d already configured\n",
		*error_out = "Device already configured";
		       n);
		return err;
		return(1);
	}
	}
	if(index_out) *index_out = n;

	*index_out = n;
	*str_out = str;
	*str_out = str;
	return(0);
	return 0;
}
}


struct eth_init {
struct eth_init {
@@ -581,11 +579,15 @@ static int eth_setup_common(char *str, int index)
static int eth_setup(char *str)
static int eth_setup(char *str)
{
{
	struct eth_init *new;
	struct eth_init *new;
	char *error;
	int n, err;
	int n, err;


	err = eth_parse(str, &n, &str);
	err = eth_parse(str, &n, &str, &error);
	if(err)
	if(err){
		printk(KERN_ERR "eth_setup - Couldn't parse '%s' : %s\n",
		       str, error);
		return 1;
		return 1;
	}


	new = alloc_bootmem(sizeof(*new));
	new = alloc_bootmem(sizeof(*new));
	if (new == NULL){
	if (new == NULL){
@@ -625,17 +627,21 @@ static int eth_init(void)
__initcall(eth_init);
__initcall(eth_init);
#endif
#endif


static int net_config(char *str)
static int net_config(char *str, char **error_out)
{
{
	int n, err;
	int n, err;


	err = eth_parse(str, &n, &str);
	err = eth_parse(str, &n, &str, error_out);
	if(err) return(err);
	if(err)
		return err;


	/* This string is broken up and the pieces used by the underlying
	 * driver.  So, it is freed only if eth_setup_common fails.
	 */
	str = kstrdup(str, GFP_KERNEL);
	str = kstrdup(str, GFP_KERNEL);
	if(str == NULL){
	if(str == NULL){
		printk(KERN_ERR "net_config failed to strdup string\n");
	        *error_out = "net_config failed to strdup string";
		return(-1);
		return -ENOMEM;
	}
	}
	err = !eth_setup_common(str, n);
	err = !eth_setup_common(str, n);
	if(err)
	if(err)
@@ -658,7 +664,7 @@ static int net_id(char **str, int *start_out, int *end_out)
        return n;
        return n;
}
}


static int net_remove(int n)
static int net_remove(int n, char **error_out)
{
{
	struct uml_net *device;
	struct uml_net *device;
	struct net_device *dev;
	struct net_device *dev;
@@ -854,14 +860,3 @@ unsigned short eth_protocol(struct sk_buff *skb)
{
{
	return(eth_type_trans(skb, skb->dev));
	return(eth_type_trans(skb, skb->dev));
}
}

/*
 * Overrides for Emacs so that we follow Linus's tabbing style.
 * Emacs will notice this stuff at the end of the file and automatically
 * adjust the settings for this buffer only.  This must remain at the end
 * of the file.
 * ---------------------------------------------------------------------------
 * Local variables:
 * c-file-style: "linux"
 * End:
 */
+17 −7
Original line number Original line Diff line number Diff line
@@ -46,9 +46,9 @@ static struct chan_opts opts = {
	.in_kernel 	= 1,
	.in_kernel 	= 1,
};
};


static int ssl_config(char *str);
static int ssl_config(char *str, char **error_out);
static int ssl_get_config(char *dev, char *str, int size, char **error_out);
static int ssl_get_config(char *dev, char *str, int size, char **error_out);
static int ssl_remove(int n);
static int ssl_remove(int n, char **error_out);


static struct line_driver driver = {
static struct line_driver driver = {
	.name 			= "UML serial line",
	.name 			= "UML serial line",
@@ -80,9 +80,10 @@ static struct line serial_lines[NR_PORTS] =


static struct lines lines = LINES_INIT(NR_PORTS);
static struct lines lines = LINES_INIT(NR_PORTS);


static int ssl_config(char *str)
static int ssl_config(char *str, char **error_out)
{
{
	return line_config(serial_lines, ARRAY_SIZE(serial_lines), str, &opts);
	return line_config(serial_lines, ARRAY_SIZE(serial_lines), str, &opts,
			   error_out);
}
}


static int ssl_get_config(char *dev, char *str, int size, char **error_out)
static int ssl_get_config(char *dev, char *str, int size, char **error_out)
@@ -91,9 +92,10 @@ static int ssl_get_config(char *dev, char *str, int size, char **error_out)
			       size, error_out);
			       size, error_out);
}
}


static int ssl_remove(int n)
static int ssl_remove(int n, char **error_out)
{
{
	return line_remove(serial_lines, ARRAY_SIZE(serial_lines), n);
	return line_remove(serial_lines, ARRAY_SIZE(serial_lines), n,
			   error_out);
}
}


static int ssl_open(struct tty_struct *tty, struct file *filp)
static int ssl_open(struct tty_struct *tty, struct file *filp)
@@ -212,7 +214,15 @@ __uml_exitcall(ssl_exit);


static int ssl_chan_setup(char *str)
static int ssl_chan_setup(char *str)
{
{
	return line_setup(serial_lines, ARRAY_SIZE(serial_lines), str);
	char *error;
	int ret;

	ret = line_setup(serial_lines, ARRAY_SIZE(serial_lines), str, &error);
	if(ret < 0)
		printk(KERN_ERR "Failed to set up serial line with "
		       "configuration string \"%s\" : %s\n", str, error);

	return 1;
}
}


__setup("ssl", ssl_chan_setup);
__setup("ssl", ssl_chan_setup);
Loading