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

[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
...@@ -19,44 +19,11 @@ ...@@ -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) ...@@ -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) ...@@ -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[] = { ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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;
......
...@@ -549,14 +549,16 @@ void close_lines(struct line *lines, int nlines) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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); ...@@ -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) ...@@ -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;
} }
} }
......
...@@ -371,14 +371,16 @@ static unsigned long long unplugged_pages_count = 0; ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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:
......
...@@ -463,35 +463,33 @@ static struct uml_net *find_device(int n) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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:
*/
...@@ -46,9 +46,9 @@ static struct chan_opts opts = { ...@@ -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] = ...@@ -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) ...@@ -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); ...@@ -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);
......
...@@ -52,9 +52,9 @@ static struct chan_opts opts = { ...@@ -52,9 +52,9 @@ static struct chan_opts opts = {
.in_kernel = 1, .in_kernel = 1,
}; };
static int con_config(char *str); static int con_config(char *str, char **error_out);
static int con_get_config(char *dev, char *str, int size, char **error_out); static int con_get_config(char *dev, char *str, int size, char **error_out);
static int con_remove(int n); static int con_remove(int n, char **con_remove);
static struct line_driver driver = { static struct line_driver driver = {
.name = "UML console", .name = "UML console",
...@@ -87,9 +87,9 @@ static struct line vts[MAX_TTYS] = { LINE_INIT(CONFIG_CON_ZERO_CHAN, &driver), ...@@ -87,9 +87,9 @@ static struct line vts[MAX_TTYS] = { LINE_INIT(CONFIG_CON_ZERO_CHAN, &driver),
[ 1 ... MAX_TTYS - 1 ] = [ 1 ... MAX_TTYS - 1 ] =
LINE_INIT(CONFIG_CON_CHAN, &driver) }; LINE_INIT(CONFIG_CON_CHAN, &driver) };
static int con_config(char *str) static int con_config(char *str, char **error_out)
{ {
return line_config(vts, ARRAY_SIZE(vts), str, &opts); return line_config(vts, ARRAY_SIZE(vts), str, &opts, error_out);
} }
static int con_get_config(char *dev, char *str, int size, char **error_out) static int con_get_config(char *dev, char *str, int size, char **error_out)
...@@ -97,9 +97,9 @@ static int con_get_config(char *dev, char *str, int size, char **error_out) ...@@ -97,9 +97,9 @@ static int con_get_config(char *dev, char *str, int size, char **error_out)
return line_get_config(dev, vts, ARRAY_SIZE(vts), str, size, error_out); return line_get_config(dev, vts, ARRAY_SIZE(vts), str, size, error_out);
} }
static int con_remove(int n) static int con_remove(int n, char **error_out)
{ {
return line_remove(vts, ARRAY_SIZE(vts), n); return line_remove(vts, ARRAY_SIZE(vts), n, error_out);
} }
static int con_open(struct tty_struct *tty, struct file *filp) static int con_open(struct tty_struct *tty, struct file *filp)
...@@ -192,7 +192,15 @@ __uml_exitcall(console_exit); ...@@ -192,7 +192,15 @@ __uml_exitcall(console_exit);
static int console_chan_setup(char *str) static int console_chan_setup(char *str)
{ {
return line_setup(vts, ARRAY_SIZE(vts), str); char *error;
int ret;
ret = line_setup(vts, ARRAY_SIZE(vts), str, &error);
if(ret < 0)
printk(KERN_ERR "Failed to set up console with "
"configuration string \"%s\" : %s\n", str, error);
return 1;
} }
__setup("con", console_chan_setup); __setup("con", console_chan_setup);
__channel_help(console_chan_setup, "con"); __channel_help(console_chan_setup, "con");
...@@ -286,7 +286,7 @@ static int parse_unit(char **ptr) ...@@ -286,7 +286,7 @@ static int parse_unit(char **ptr)
* otherwise, the str pointer is used (and owned) inside ubd_devs array, so it * otherwise, the str pointer is used (and owned) inside ubd_devs array, so it
* should not be freed on exit. * should not be freed on exit.
*/ */
static int ubd_setup_common(char *str, int *index_out) static int ubd_setup_common(char *str, int *index_out, char **error_out)
{ {
struct ubd *ubd_dev; struct ubd *ubd_dev;
struct openflags flags = global_openflags; struct openflags flags = global_openflags;
...@@ -302,19 +302,18 @@ static int ubd_setup_common(char *str, int *index_out) ...@@ -302,19 +302,18 @@ static int ubd_setup_common(char *str, int *index_out)
str++; str++;
if(!strcmp(str, "sync")){ if(!strcmp(str, "sync")){
global_openflags = of_sync(global_openflags); global_openflags = of_sync(global_openflags);
return(0); return 0;
} }
major = simple_strtoul(str, &end, 0); major = simple_strtoul(str, &end, 0);
if((*end != '\0') || (end == str)){ if((*end != '\0') || (end == str)){
printk(KERN_ERR *error_out = "Didn't parse major number";
"ubd_setup : didn't parse major number\n"); return -EINVAL;
return(1);
} }
err = 1; err = -EINVAL;
mutex_lock(&ubd_lock); mutex_lock(&ubd_lock);
if(fake_major != MAJOR_NR){ if(fake_major != MAJOR_NR){
printk(KERN_ERR "Can't assign a fake major twice\n"); *error_out = "Can't assign a fake major twice";
goto out1; goto out1;
} }
...@@ -325,33 +324,32 @@ static int ubd_setup_common(char *str, int *index_out) ...@@ -325,33 +324,32 @@ static int ubd_setup_common(char *str, int *index_out)
err = 0; err = 0;
out1: out1:
mutex_unlock(&ubd_lock); mutex_unlock(&ubd_lock);
return(err); return err;
} }
n = parse_unit(&str); n = parse_unit(&str);
if(n < 0){ if(n < 0){
printk(KERN_ERR "ubd_setup : couldn't parse unit number " *error_out = "Couldn't parse device number";
"'%s'\n", str); return -EINVAL;
return(1);
} }
if(n >= MAX_DEV){ if(n >= MAX_DEV){
printk(KERN_ERR "ubd_setup : index %d out of range " *error_out = "Device number out of range";
"(%d devices, from 0 to %d)\n", n, MAX_DEV, MAX_DEV - 1); return 1;
return(1);
} }
err = 1; err = -EBUSY;
mutex_lock(&ubd_lock); mutex_lock(&ubd_lock);
ubd_dev = &ubd_devs[n]; ubd_dev = &ubd_devs[n];
if(ubd_dev->file != NULL){ if(ubd_dev->file != NULL){
printk(KERN_ERR "ubd_setup : device already configured\n"); *error_out = "Device is already configured";
goto out; goto out;
} }
if (index_out) if (index_out)
*index_out = n; *index_out = n;
err = -EINVAL;
for (i = 0; i < sizeof("rscd="); i++) { for (i = 0; i < sizeof("rscd="); i++) {
switch (*str) { switch (*str) {
case 'r': case 'r':
...@@ -370,47 +368,54 @@ static int ubd_setup_common(char *str, int *index_out) ...@@ -370,47 +368,54 @@ static int ubd_setup_common(char *str, int *index_out)
str++; str++;
goto break_loop; goto break_loop;
default: default:
printk(KERN_ERR "ubd_setup : Expected '=' or flag letter (r, s, c, or d)\n"); *error_out = "Expected '=' or flag letter "
"(r, s, c, or d)";
goto out; goto out;
} }
str++; str++;
} }
if (*str == '=') if (*str == '=')
printk(KERN_ERR "ubd_setup : Too many flags specified\n"); *error_out = "Too many flags specified";
else else
printk(KERN_ERR "ubd_setup : Expected '='\n"); *error_out = "Missing '='";
goto out; goto out;
break_loop: break_loop:
err = 0;
backing_file = strchr(str, ','); backing_file = strchr(str, ',');
if (!backing_file) { if (backing_file == NULL)
backing_file = strchr(str, ':'); backing_file = strchr(str, ':');
}
if(backing_file){ if(backing_file != NULL){
if(ubd_dev->no_cow) if(ubd_dev->no_cow){
printk(KERN_ERR "Can't specify both 'd' and a " *error_out = "Can't specify both 'd' and a cow file";
"cow file\n"); goto out;
}
else { else {
*backing_file = '\0'; *backing_file = '\0';
backing_file++; backing_file++;
} }
} }
err = 0;
ubd_dev->file = str; ubd_dev->file = str;
ubd_dev->cow.file = backing_file; ubd_dev->cow.file = backing_file;
ubd_dev->boot_openflags = flags; ubd_dev->boot_openflags = flags;
out: out:
mutex_unlock(&ubd_lock); mutex_unlock(&ubd_lock);
return(err); return err;
} }
static int ubd_setup(char *str) static int ubd_setup(char *str)
{ {
ubd_setup_common(str, NULL); char *error;
return(1); int err;
err = ubd_setup_common(str, NULL, &error);
if(err)
printk(KERN_ERR "Failed to initialize device with \"%s\" : "
"%s\n", str, error);
return 1;
} }
__setup("ubd", ubd_setup); __setup("ubd", ubd_setup);
...@@ -422,7 +427,7 @@ __uml_help(ubd_setup, ...@@ -422,7 +427,7 @@ __uml_help(ubd_setup,
" use either a ':' or a ',': the first one allows writing things like;\n" " use either a ':' or a ',': the first one allows writing things like;\n"
" ubd0=~/Uml/root_cow:~/Uml/root_backing_file\n" " ubd0=~/Uml/root_cow:~/Uml/root_backing_file\n"
" while with a ',' the shell would not expand the 2nd '~'.\n" " while with a ',' the shell would not expand the 2nd '~'.\n"
" When using only one filename, UML will detect whether to thread it like\n" " When using only one filename, UML will detect whether to treat it like\n"
" a COW file or a backing file. To override this detection, add the 'd'\n" " a COW file or a backing file. To override this detection, add the 'd'\n"
" flag:\n" " flag:\n"
" ubd0d=BackingFile\n" " ubd0d=BackingFile\n"
...@@ -668,18 +673,19 @@ static int ubd_disk_register(int major, u64 size, int unit, ...@@ -668,18 +673,19 @@ static int ubd_disk_register(int major, u64 size, int unit,
#define ROUND_BLOCK(n) ((n + ((1 << 9) - 1)) & (-1 << 9)) #define ROUND_BLOCK(n) ((n + ((1 << 9) - 1)) & (-1 << 9))
static int ubd_add(int n) static int ubd_add(int n, char **error_out)
{ {
struct ubd *ubd_dev = &ubd_devs[n]; struct ubd *ubd_dev = &ubd_devs[n];
int err; int err = 0;
err = -ENODEV;
if(ubd_dev->file == NULL) if(ubd_dev->file == NULL)
goto out; goto out;
err = ubd_file_size(ubd_dev, &ubd_dev->size); err = ubd_file_size(ubd_dev, &ubd_dev->size);
if(err < 0) if(err < 0){
*error_out = "Couldn't determine size of device's file";
goto out; goto out;
}
ubd_dev->size = ROUND_BLOCK(ubd_dev->size); ubd_dev->size = ROUND_BLOCK(ubd_dev->size);
...@@ -701,28 +707,31 @@ static int ubd_add(int n) ...@@ -701,28 +707,31 @@ static int ubd_add(int n)
return err; return err;
} }
static int ubd_config(char *str) static int ubd_config(char *str, char **error_out)
{ {
int n, ret; int n, ret;
/* This string is possibly broken up and stored, so it's only
* freed if ubd_setup_common fails, or if only general options
* were set.
*/
str = kstrdup(str, GFP_KERNEL); str = kstrdup(str, GFP_KERNEL);
if (str == NULL) { if (str == NULL) {
printk(KERN_ERR "ubd_config failed to strdup string\n"); *error_out = "Failed to allocate memory";
ret = 1; return -ENOMEM;
goto out;
} }
ret = ubd_setup_common(str, &n);
if (ret) { ret = ubd_setup_common(str, &n, error_out);
ret = -1; if (ret)
goto err_free; goto err_free;
}
if (n == -1) { if (n == -1) {
ret = 0; ret = 0;
goto err_free; goto err_free;
} }
mutex_lock(&ubd_lock); mutex_lock(&ubd_lock);
ret = ubd_add(n); ret = ubd_add(n, error_out);
if (ret) if (ret)
ubd_devs[n].file = NULL; ubd_devs[n].file = NULL;
mutex_unlock(&ubd_lock); mutex_unlock(&ubd_lock);
...@@ -777,7 +786,7 @@ static int ubd_id(char **str, int *start_out, int *end_out) ...@@ -777,7 +786,7 @@ static int ubd_id(char **str, int *start_out, int *end_out)
return n; return n;
} }
static int ubd_remove(int n) static int ubd_remove(int n, char **error_out)
{ {
struct ubd *ubd_dev; struct ubd *ubd_dev;
int err = -ENODEV; int err = -ENODEV;
...@@ -815,7 +824,9 @@ static int ubd_remove(int n) ...@@ -815,7 +824,9 @@ static int ubd_remove(int n)
return err; return err;
} }
/* All these are called by mconsole in process context and without ubd-specific locks. */ /* All these are called by mconsole in process context and without
* ubd-specific locks.
*/
static struct mc_device ubd_mc = { static struct mc_device ubd_mc = {
.name = "ubd", .name = "ubd",
.config = ubd_config, .config = ubd_config,
...@@ -851,7 +862,8 @@ static struct platform_driver ubd_driver = { ...@@ -851,7 +862,8 @@ static struct platform_driver ubd_driver = {
static int __init ubd_init(void) static int __init ubd_init(void)
{ {
int i; char *error;
int i, err;
if (register_blkdev(MAJOR_NR, "ubd")) if (register_blkdev(MAJOR_NR, "ubd"))
return -1; return -1;
...@@ -870,8 +882,12 @@ static int __init ubd_init(void) ...@@ -870,8 +882,12 @@ static int __init ubd_init(void)
return -1; return -1;
} }
platform_driver_register(&ubd_driver); platform_driver_register(&ubd_driver);
for (i = 0; i < MAX_DEV; i++) for (i = 0; i < MAX_DEV; i++){
ubd_add(i); err = ubd_add(i, &error);
if(err)
printk(KERN_ERR "Failed to initialize ubd device %d :"
"%s\n", i, error);
}
return 0; return 0;
} }
......
...@@ -30,7 +30,7 @@ struct chan { ...@@ -30,7 +30,7 @@ struct chan {
extern void chan_interrupt(struct list_head *chans, struct delayed_work *task, extern void chan_interrupt(struct list_head *chans, struct delayed_work *task,
struct tty_struct *tty, int irq); struct tty_struct *tty, int irq);
extern int parse_chan_pair(char *str, struct line *line, int device, extern int parse_chan_pair(char *str, struct line *line, int device,
const struct chan_opts *opts); const struct chan_opts *opts, char **error_out);
extern int open_chan(struct list_head *chans); extern int open_chan(struct list_head *chans);
extern int write_chan(struct list_head *chans, const char *buf, int len, extern int write_chan(struct list_head *chans, const char *buf, int len,
int write_irq); int write_irq);
......
...@@ -76,7 +76,7 @@ struct lines { ...@@ -76,7 +76,7 @@ struct lines {
extern void line_close(struct tty_struct *tty, struct file * filp); extern void line_close(struct tty_struct *tty, struct file * filp);
extern int line_open(struct line *lines, struct tty_struct *tty); extern int line_open(struct line *lines, struct tty_struct *tty);
extern int line_setup(struct line *lines, unsigned int sizeof_lines, extern int line_setup(struct line *lines, unsigned int sizeof_lines,
char *init); char *init, char **error_out);
extern int line_write(struct tty_struct *tty, const unsigned char *buf, extern int line_write(struct tty_struct *tty, const unsigned char *buf,
int len); int len);
extern void line_put_char(struct tty_struct *tty, unsigned char ch); extern void line_put_char(struct tty_struct *tty, unsigned char ch);
...@@ -102,9 +102,11 @@ extern void lines_init(struct line *lines, int nlines, struct chan_opts *opts); ...@@ -102,9 +102,11 @@ extern void lines_init(struct line *lines, int nlines, struct chan_opts *opts);
extern void close_lines(struct line *lines, int nlines); extern void close_lines(struct line *lines, int nlines);
extern int line_config(struct line *lines, unsigned int sizeof_lines, extern int line_config(struct line *lines, unsigned int sizeof_lines,
char *str, const struct chan_opts *opts); char *str, const struct chan_opts *opts,
char **error_out);
extern int line_id(char **str, int *start_out, int *end_out); extern int line_id(char **str, int *start_out, int *end_out);
extern int line_remove(struct line *lines, unsigned int sizeof_lines, int n); extern int line_remove(struct line *lines, unsigned int sizeof_lines, int n,
char **error_out);
extern int line_get_config(char *dev, struct line *lines, extern int line_get_config(char *dev, struct line *lines,
unsigned int sizeof_lines, char *str, unsigned int sizeof_lines, char *str,
int size, char **error_out); int size, char **error_out);
......
...@@ -18,10 +18,10 @@ struct mconsole_entry { ...@@ -18,10 +18,10 @@ struct mconsole_entry {
struct mc_device { struct mc_device {
struct list_head list; struct list_head list;
char *name; char *name;
int (*config)(char *); int (*config)(char *, char **);
int (*get_config)(char *, char *, int, char **); int (*get_config)(char *, char *, int, char **);
int (*id)(char **, int *, int *); int (*id)(char **, int *, int *);
int (*remove)(int); int (*remove)(int, char **);
}; };
#define CONFIG_CHUNK(str, size, current, chunk, end) \ #define CONFIG_CHUNK(str, size, current, chunk, end) \
...@@ -50,14 +50,3 @@ static inline void mconsole_register_dev(struct mc_device *new) ...@@ -50,14 +50,3 @@ static inline void mconsole_register_dev(struct mc_device *new)
#endif #endif
#endif #endif
/*
* 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:
*/
...@@ -139,7 +139,7 @@ static void config_gdb_cb(void *arg) ...@@ -139,7 +139,7 @@ static void config_gdb_cb(void *arg)
init_proxy(debugger_pid, 0, 0); init_proxy(debugger_pid, 0, 0);
} }
int gdb_config(char *str) int gdb_config(char *str, char **error_out)
{ {
struct gdb_data data; struct gdb_data data;
...@@ -154,7 +154,7 @@ void remove_gdb_cb(void *unused) ...@@ -154,7 +154,7 @@ void remove_gdb_cb(void *unused)
exit_debugger_cb(NULL); exit_debugger_cb(NULL);
} }
int gdb_remove(int unused) int gdb_remove(int unused, char **error_out)
{ {
initial_thread_cb(remove_gdb_cb, NULL); initial_thread_cb(remove_gdb_cb, NULL);
return 0; return 0;
......
...@@ -8,8 +8,8 @@ ...@@ -8,8 +8,8 @@
#ifdef CONFIG_MCONSOLE #ifdef CONFIG_MCONSOLE
extern int gdb_config(char *str); extern int gdb_config(char *str, char **error_out);
extern int gdb_remove(int n); extern int gdb_remove(int n, char **error_out);
static struct mc_device gdb_mc = { static struct mc_device gdb_mc = {
.name = "gdb", .name = "gdb",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment