Commit fa453a62 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge branch 'for-linus-3.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rw/uml

Pull UML changes from Richard Weinberger:
 "Mostly bug fixes and cleanups"

* 'for-linus-3.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rw/uml: (35 commits)
  um: Update defconfig
  um: Switch to large mcmodel on x86_64
  MTD: Relax dependencies
  um: Wire CONFIG_GENERIC_IO up
  um: Serve io_remap_pfn_range()
  Introduce CONFIG_GENERIC_IO
  um: allow SUBARCH=x86
  um: most of the SUBARCH uses can be killed
  um: deadlock in line_write_interrupt()
  um: don't bother trying to rebuild CHECKFLAGS for USER_OBJS
  um: use the right ifdef around exports in user_syms.c
  um: a bunch of headers can be killed by using generic-y
  um: ptrace-generic.h doesn't need user.h
  um: kill HOST_TASK_PID
  um: remove pointless include of asm/fixmap.h from asm/pgtable.h
  um: asm-offsets.h might as well come from underlying arch...
  um: merge processor_{32,64}.h a bit...
  um: switch close_chan() to struct line
  um: race fix: initialize delayed_work *before* registering IRQ
  um: line->have_irq is never checked...
  ...
parents 30eebb54 3463ff44
......@@ -9,6 +9,7 @@ config UML
select HAVE_GENERIC_HARDIRQS
select GENERIC_IRQ_SHOW
select GENERIC_CPU_DEVICES
select GENERIC_IO
config MMU
bool
......
......@@ -28,6 +28,7 @@ ifeq ($(SUBARCH),i386)
endif
ifeq ($(SUBARCH),x86_64)
HEADER_ARCH := x86
KBUILD_CFLAGS += -mcmodel=large
endif
HOST_DIR := arch/$(HEADER_ARCH)
......@@ -50,7 +51,7 @@ KBUILD_CPPFLAGS += -I$(srctree)/$(HOST_DIR)/um
#
# These apply to USER_CFLAGS to.
KBUILD_CFLAGS += $(CFLAGS) $(CFLAGS-y) -D__arch_um__ -DSUBARCH=\"$(SUBARCH)\" \
KBUILD_CFLAGS += $(CFLAGS) $(CFLAGS-y) -D__arch_um__ \
$(ARCH_INCLUDE) $(MODE_INCLUDE) -Dvmap=kernel_vmap \
-Din6addr_loopback=kernel_in6addr_loopback \
-Din6addr_any=kernel_in6addr_any -Dstrrchr=kernel_strrchr
......@@ -99,7 +100,7 @@ KBUILD_KCONFIG := $(HOST_DIR)/um/Kconfig
archheaders:
$(Q)$(MAKE) -C '$(srctree)' KBUILD_SRC= \
ARCH=$(SUBARCH) O='$(objtree)' archheaders
ARCH=$(HEADER_ARCH) O='$(objtree)' archheaders
archprepare: include/generated/user_constants.h
......
This diff is collapsed.
......@@ -27,24 +27,24 @@ struct chan {
void *data;
};
extern void chan_interrupt(struct list_head *chans, struct delayed_work *task,
extern void chan_interrupt(struct line *line,
struct tty_struct *tty, int irq);
extern int parse_chan_pair(char *str, struct line *line, int device,
const struct chan_opts *opts, char **error_out);
extern int write_chan(struct list_head *chans, const char *buf, int len,
extern int write_chan(struct chan *chan, const char *buf, int len,
int write_irq);
extern int console_write_chan(struct list_head *chans, const char *buf,
extern int console_write_chan(struct chan *chan, const char *buf,
int len);
extern int console_open_chan(struct line *line, struct console *co);
extern void deactivate_chan(struct list_head *chans, int irq);
extern void reactivate_chan(struct list_head *chans, int irq);
extern void chan_enable_winch(struct list_head *chans, struct tty_struct *tty);
extern void deactivate_chan(struct chan *chan, int irq);
extern void reactivate_chan(struct chan *chan, int irq);
extern void chan_enable_winch(struct chan *chan, struct tty_struct *tty);
extern int enable_chan(struct line *line);
extern void close_chan(struct list_head *chans, int delay_free_irq);
extern int chan_window_size(struct list_head *chans,
extern void close_chan(struct line *line);
extern int chan_window_size(struct line *line,
unsigned short *rows_out,
unsigned short *cols_out);
extern int chan_config_string(struct list_head *chans, char *str, int size,
extern int chan_config_string(struct line *line, char *str, int size,
char **error_out);
#endif
......@@ -140,18 +140,18 @@ static int open_chan(struct list_head *chans)
return err;
}
void chan_enable_winch(struct list_head *chans, struct tty_struct *tty)
void chan_enable_winch(struct chan *chan, struct tty_struct *tty)
{
struct list_head *ele;
struct chan *chan;
if (chan && chan->primary && chan->ops->winch)
register_winch(chan->fd, tty);
}
list_for_each(ele, chans) {
chan = list_entry(ele, struct chan, list);
if (chan->primary && chan->output && chan->ops->winch) {
register_winch(chan->fd, tty);
return;
}
}
static void line_timer_cb(struct work_struct *work)
{
struct line *line = container_of(work, struct line, task.work);
if (!line->throttled)
chan_interrupt(line, line->tty, line->driver->read_irq);
}
int enable_chan(struct line *line)
......@@ -160,6 +160,8 @@ int enable_chan(struct line *line)
struct chan *chan;
int err;
INIT_DELAYED_WORK(&line->task, line_timer_cb);
list_for_each(ele, &line->chan_list) {
chan = list_entry(ele, struct chan, list);
err = open_one_chan(chan);
......@@ -183,7 +185,7 @@ int enable_chan(struct line *line)
return 0;
out_close:
close_chan(&line->chan_list, 0);
close_chan(line);
return err;
}
......@@ -244,7 +246,7 @@ static void close_one_chan(struct chan *chan, int delay_free_irq)
chan->fd = -1;
}
void close_chan(struct list_head *chans, int delay_free_irq)
void close_chan(struct line *line)
{
struct chan *chan;
......@@ -253,77 +255,50 @@ void close_chan(struct list_head *chans, int delay_free_irq)
* state. Then, the first one opened will have the original state,
* so it must be the last closed.
*/
list_for_each_entry_reverse(chan, chans, list) {
close_one_chan(chan, delay_free_irq);
list_for_each_entry_reverse(chan, &line->chan_list, list) {
close_one_chan(chan, 0);
}
}
void deactivate_chan(struct list_head *chans, int irq)
void deactivate_chan(struct chan *chan, int irq)
{
struct list_head *ele;
struct chan *chan;
list_for_each(ele, chans) {
chan = list_entry(ele, struct chan, list);
if (chan->enabled && chan->input)
deactivate_fd(chan->fd, irq);
}
if (chan && chan->enabled)
deactivate_fd(chan->fd, irq);
}
void reactivate_chan(struct list_head *chans, int irq)
void reactivate_chan(struct chan *chan, int irq)
{
struct list_head *ele;
struct chan *chan;
list_for_each(ele, chans) {
chan = list_entry(ele, struct chan, list);
if (chan->enabled && chan->input)
reactivate_fd(chan->fd, irq);
}
if (chan && chan->enabled)
reactivate_fd(chan->fd, irq);
}
int write_chan(struct list_head *chans, const char *buf, int len,
int write_chan(struct chan *chan, const char *buf, int len,
int write_irq)
{
struct list_head *ele;
struct chan *chan = NULL;
int n, ret = 0;
if (len == 0)
if (len == 0 || !chan || !chan->ops->write)
return 0;
list_for_each(ele, chans) {
chan = list_entry(ele, struct chan, list);
if (!chan->output || (chan->ops->write == NULL))
continue;
n = chan->ops->write(chan->fd, buf, len, chan->data);
if (chan->primary) {
ret = n;
if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len)))
reactivate_fd(chan->fd, write_irq);
}
n = chan->ops->write(chan->fd, buf, len, chan->data);
if (chan->primary) {
ret = n;
if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len)))
reactivate_fd(chan->fd, write_irq);
}
return ret;
}
int console_write_chan(struct list_head *chans, const char *buf, int len)
int console_write_chan(struct chan *chan, const char *buf, int len)
{
struct list_head *ele;
struct chan *chan;
int n, ret = 0;
list_for_each(ele, chans) {
chan = list_entry(ele, struct chan, list);
if (!chan->output || (chan->ops->console_write == NULL))
continue;
if (!chan || !chan->ops->console_write)
return 0;
n = chan->ops->console_write(chan->fd, buf, len);
if (chan->primary)
ret = n;
}
n = chan->ops->console_write(chan->fd, buf, len);
if (chan->primary)
ret = n;
return ret;
}
......@@ -340,20 +315,24 @@ int console_open_chan(struct line *line, struct console *co)
return 0;
}
int chan_window_size(struct list_head *chans, unsigned short *rows_out,
int chan_window_size(struct line *line, unsigned short *rows_out,
unsigned short *cols_out)
{
struct list_head *ele;
struct chan *chan;
list_for_each(ele, chans) {
chan = list_entry(ele, struct chan, list);
if (chan->primary) {
if (chan->ops->window_size == NULL)
return 0;
return chan->ops->window_size(chan->fd, chan->data,
rows_out, cols_out);
}
chan = line->chan_in;
if (chan && chan->primary) {
if (chan->ops->window_size == NULL)
return 0;
return chan->ops->window_size(chan->fd, chan->data,
rows_out, cols_out);
}
chan = line->chan_out;
if (chan && chan->primary) {
if (chan->ops->window_size == NULL)
return 0;
return chan->ops->window_size(chan->fd, chan->data,
rows_out, cols_out);
}
return 0;
}
......@@ -429,21 +408,15 @@ static int chan_pair_config_string(struct chan *in, struct chan *out,
return n;
}
int chan_config_string(struct list_head *chans, char *str, int size,
int chan_config_string(struct line *line, char *str, int size,
char **error_out)
{
struct list_head *ele;
struct chan *chan, *in = NULL, *out = NULL;
struct chan *in = line->chan_in, *out = line->chan_out;
list_for_each(ele, chans) {
chan = list_entry(ele, struct chan, list);
if (!chan->primary)
continue;
if (chan->input)
in = chan;
if (chan->output)
out = chan;
}
if (in && !in->primary)
in = NULL;
if (out && !out->primary)
out = NULL;
return chan_pair_config_string(in, out, str, size, error_out);
}
......@@ -547,10 +520,14 @@ int parse_chan_pair(char *str, struct line *line, int device,
char *in, *out;
if (!list_empty(chans)) {
line->chan_in = line->chan_out = NULL;
free_chan(chans);
INIT_LIST_HEAD(chans);
}
if (!str)
return 0;
out = strchr(str, ',');
if (out != NULL) {
in = str;
......@@ -562,6 +539,7 @@ int parse_chan_pair(char *str, struct line *line, int device,
new->input = 1;
list_add(&new->list, chans);
line->chan_in = new;
new = parse_chan(line, out, device, opts, error_out);
if (new == NULL)
......@@ -569,6 +547,7 @@ int parse_chan_pair(char *str, struct line *line, int device,
list_add(&new->list, chans);
new->output = 1;
line->chan_out = new;
}
else {
new = parse_chan(line, str, device, opts, error_out);
......@@ -578,43 +557,42 @@ int parse_chan_pair(char *str, struct line *line, int device,
list_add(&new->list, chans);
new->input = 1;
new->output = 1;
line->chan_in = line->chan_out = new;
}
return 0;
}
void chan_interrupt(struct list_head *chans, struct delayed_work *task,
struct tty_struct *tty, int irq)
void chan_interrupt(struct line *line, struct tty_struct *tty, int irq)
{
struct list_head *ele, *next;
struct chan *chan;
struct chan *chan = line->chan_in;
int err;
char c;
list_for_each_safe(ele, next, chans) {
chan = list_entry(ele, struct chan, list);
if (!chan->input || (chan->ops->read == NULL))
continue;
do {
if (tty && !tty_buffer_request_room(tty, 1)) {
schedule_delayed_work(task, 1);
goto out;
}
err = chan->ops->read(chan->fd, &c, chan->data);
if (err > 0)
tty_receive_char(tty, c);
} while (err > 0);
if (err == 0)
reactivate_fd(chan->fd, irq);
if (err == -EIO) {
if (chan->primary) {
if (tty != NULL)
tty_hangup(tty);
close_chan(chans, 1);
return;
}
else close_one_chan(chan, 1);
if (!chan || !chan->ops->read)
goto out;
do {
if (tty && !tty_buffer_request_room(tty, 1)) {
schedule_delayed_work(&line->task, 1);
goto out;
}
err = chan->ops->read(chan->fd, &c, chan->data);
if (err > 0)
tty_receive_char(tty, c);
} while (err > 0);
if (err == 0)
reactivate_fd(chan->fd, irq);
if (err == -EIO) {
if (chan->primary) {
if (tty != NULL)
tty_hangup(tty);
if (line->chan_out != chan)
close_one_chan(line->chan_out, 1);
}
close_one_chan(chan, 1);
if (chan->primary)
return;
}
out:
if (tty)
......
......@@ -14,8 +14,6 @@ struct chan_opts {
const int raw;
};
enum chan_init_pri { INIT_STATIC, INIT_ALL, INIT_ONE };
struct chan_ops {
char *type;
void *(*init)(char *, int, const struct chan_opts *);
......
......@@ -21,19 +21,10 @@ static irqreturn_t line_interrupt(int irq, void *data)
struct line *line = chan->line;
if (line)
chan_interrupt(&line->chan_list, &line->task, line->tty, irq);
chan_interrupt(line, line->tty, irq);
return IRQ_HANDLED;
}
static void line_timer_cb(struct work_struct *work)
{
struct line *line = container_of(work, struct line, task.work);
if (!line->throttled)
chan_interrupt(&line->chan_list, &line->task, line->tty,
line->driver->read_irq);
}
/*
* Returns the free space inside the ring buffer of this line.
*
......@@ -145,7 +136,7 @@ static int flush_buffer(struct line *line)
/* line->buffer + LINE_BUFSIZE is the end of the buffer! */
count = line->buffer + LINE_BUFSIZE - line->head;
n = write_chan(&line->chan_list, line->head, count,
n = write_chan(line->chan_out, line->head, count,
line->driver->write_irq);
if (n < 0)
return n;
......@@ -162,7 +153,7 @@ static int flush_buffer(struct line *line)
}
count = line->tail - line->head;
n = write_chan(&line->chan_list, line->head, count,
n = write_chan(line->chan_out, line->head, count,
line->driver->write_irq);
if (n < 0)
......@@ -206,7 +197,7 @@ int line_write(struct tty_struct *tty, const unsigned char *buf, int len)
if (line->head != line->tail)
ret = buffer_data(line, buf, len);
else {
n = write_chan(&line->chan_list, buf, len,
n = write_chan(line->chan_out, buf, len,
line->driver->write_irq);
if (n < 0) {
ret = n;
......@@ -318,7 +309,7 @@ void line_throttle(struct tty_struct *tty)
{
struct line *line = tty->driver_data;
deactivate_chan(&line->chan_list, line->driver->read_irq);
deactivate_chan(line->chan_in, line->driver->read_irq);
line->throttled = 1;
}
......@@ -327,8 +318,7 @@ void line_unthrottle(struct tty_struct *tty)
struct line *line = tty->driver_data;
line->throttled = 0;
chan_interrupt(&line->chan_list, &line->task, tty,
line->driver->read_irq);
chan_interrupt(line, tty, line->driver->read_irq);
/*
* Maybe there is enough stuff pending that calling the interrupt
......@@ -336,7 +326,7 @@ void line_unthrottle(struct tty_struct *tty)
* again and we shouldn't turn the interrupt back on.
*/
if (!line->throttled)
reactivate_chan(&line->chan_list, line->driver->read_irq);
reactivate_chan(line->chan_in, line->driver->read_irq);
}
static irqreturn_t line_write_interrupt(int irq, void *data)
......@@ -347,13 +337,14 @@ static irqreturn_t line_write_interrupt(int irq, void *data)
int err;
/*
* Interrupts are disabled here because we registered the interrupt with
* IRQF_DISABLED (see line_setup_irq).
* Interrupts are disabled here because genirq keep irqs disabled when
* calling the action handler.
*/
spin_lock(&line->lock);
err = flush_buffer(line);
if (err == 0) {
spin_unlock(&line->lock);
return IRQ_NONE;
} else if (err < 0) {
line->head = line->buffer;
......@@ -371,7 +362,7 @@ static irqreturn_t line_write_interrupt(int irq, void *data)
int line_setup_irq(int fd, int input, int output, struct line *line, void *data)
{
const struct line_driver *driver = line->driver;
int err = 0, flags = IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM;
int err = 0, flags = IRQF_SHARED | IRQF_SAMPLE_RANDOM;
if (input)
err = um_request_irq(driver->read_irq, fd, IRQ_READ,
......@@ -383,7 +374,6 @@ int line_setup_irq(int fd, int input, int output, struct line *line, void *data)
err = um_request_irq(driver->write_irq, fd, IRQ_WRITE,
line_write_interrupt, flags,
driver->write_irq_name, data);
line->have_irq = 1;
return err;
}
......@@ -409,7 +399,7 @@ int line_open(struct line *lines, struct tty_struct *tty)
struct line *line = &lines[tty->index];
int err = -ENODEV;
spin_lock(&line->count_lock);
mutex_lock(&line->count_lock);
if (!line->valid)
goto out_unlock;
......@@ -421,25 +411,19 @@ int line_open(struct line *lines, struct tty_struct *tty)
tty->driver_data = line;
line->tty = tty;
spin_unlock(&line->count_lock);
err = enable_chan(line);
if (err) /* line_close() will be called by our caller */
return err;
INIT_DELAYED_WORK(&line->task, line_timer_cb);
goto out_unlock;
if (!line->sigio) {
chan_enable_winch(&line->chan_list, tty);
chan_enable_winch(line->chan_out, tty);
line->sigio = 1;
}
chan_window_size(&line->chan_list, &tty->winsize.ws_row,
chan_window_size(line, &tty->winsize.ws_row,
&tty->winsize.ws_col);
return 0;
out_unlock:
spin_unlock(&line->count_lock);
mutex_unlock(&line->count_lock);
return err;
}
......@@ -459,7 +443,7 @@ void line_close(struct tty_struct *tty, struct file * filp)
/* We ignore the error anyway! */
flush_buffer(line);
spin_lock(&line->count_lock);
mutex_lock(&line->count_lock);
BUG_ON(!line->valid);
if (--line->count)
......@@ -468,17 +452,13 @@ void line_close(struct tty_struct *tty, struct file * filp)
line->tty = NULL;
tty->driver_data = NULL;
spin_unlock(&line->count_lock);
if (line->sigio) {
unregister_winch(tty);
line->sigio = 0;
}
return;
out_unlock:
spin_unlock(&line->count_lock);
mutex_unlock(&line->count_lock);
}
void close_lines(struct line *lines, int nlines)
......@@ -486,34 +466,60 @@ void close_lines(struct line *lines, int nlines)
int i;
for(i = 0; i < nlines; i++)
close_chan(&lines[i].chan_list, 0);
close_chan(&lines[i]);
}
static int setup_one_line(struct line *lines, int n, char *init, int init_prio,
char **error_out)
int setup_one_line(struct line *lines, int n, char *init,
const struct chan_opts *opts, char **error_out)
{
struct line *line = &lines[n];
struct tty_driver *driver = line->driver->driver;
int err = -EINVAL;
spin_lock(&line->count_lock);
mutex_lock(&line->count_lock);
if (line->count) {
*error_out = "Device is already open";
goto out;
}
if (line->init_pri <= init_prio) {
line->init_pri = init_prio;
if (!strcmp(init, "none"))
if (!strcmp(init, "none")) {
if (line->valid) {
line->valid = 0;
kfree(line->init_str);
tty_unregister_device(driver, n);
parse_chan_pair(NULL, line, n, opts, error_out);
err = 0;
}
} else {
char *new = kstrdup(init, GFP_KERNEL);
if (!new) {
*error_out = "Failed to allocate memory";
return -ENOMEM;
}
if (line->valid) {