2005-10-25 22:05:33

by Blaisorblade

[permalink] [raw]
Subject: [PATCH 01/11] uml: sigio code - reduce spinlock hold time

From: Paolo 'Blaisorblade' Giarrusso <[email protected]>

In a previous patch I shifted an allocation to being atomic.

In this patch, a better but more intrusive solution is implemented, i.e. hold
the lock only when really needing it, especially not over pipe operations, nor
over the culprit allocation.

Additionally, while at it, add a missing kfree in the failure path, and make
sure that if we fail in forking, write_sigio_pid is -1 and not, say, -ENOMEM.

And fix whitespace, at least for things I was touching anyway.

Signed-off-by: Paolo 'Blaisorblade' Giarrusso <[email protected]>
---

arch/um/kernel/sigio_user.c | 84 ++++++++++++++++++++++++++++++-------------
1 files changed, 59 insertions(+), 25 deletions(-)

diff --git a/arch/um/kernel/sigio_user.c b/arch/um/kernel/sigio_user.c
--- a/arch/um/kernel/sigio_user.c
+++ b/arch/um/kernel/sigio_user.c
@@ -336,70 +336,104 @@ int ignore_sigio_fd(int fd)
return(err);
}

-static int setup_initial_poll(int fd)
+static struct pollfd* setup_initial_poll(int fd)
{
struct pollfd *p;

- p = um_kmalloc_atomic(sizeof(struct pollfd));
- if(p == NULL){
+ p = um_kmalloc(sizeof(struct pollfd));
+ if (p == NULL) {
printk("setup_initial_poll : failed to allocate poll\n");
- return(-1);
+ return NULL;
}
*p = ((struct pollfd) { .fd = fd,
.events = POLLIN,
.revents = 0 });
- current_poll = ((struct pollfds) { .poll = p,
- .used = 1,
- .size = 1 });
- return(0);
+ return p;
}

void write_sigio_workaround(void)
{
unsigned long stack;
+ struct pollfd *p;
int err;
+ int l_write_sigio_fds[2];
+ int l_sigio_private[2];
+ int l_write_sigio_pid;

+ /* We call this *tons* of times - and most ones we must just fail. */
sigio_lock();
- if(write_sigio_pid != -1)
- goto out;
+ l_write_sigio_pid = write_sigio_pid;
+ sigio_unlock();
+
+ if (l_write_sigio_pid != -1)
+ return;

- err = os_pipe(write_sigio_fds, 1, 1);
+ err = os_pipe(l_write_sigio_fds, 1, 1);
if(err < 0){
printk("write_sigio_workaround - os_pipe 1 failed, "
"err = %d\n", -err);
- goto out;
+ return;
}
- err = os_pipe(sigio_private, 1, 1);
+ err = os_pipe(l_sigio_private, 1, 1);
if(err < 0){
- printk("write_sigio_workaround - os_pipe 2 failed, "
+ printk("write_sigio_workaround - os_pipe 1 failed, "
"err = %d\n", -err);
goto out_close1;
}
- if(setup_initial_poll(sigio_private[1]))
+
+ p = setup_initial_poll(l_sigio_private[1]);
+ if(!p)
goto out_close2;

- write_sigio_pid = run_helper_thread(write_sigio_thread, NULL,
+ sigio_lock();
+
+ /* Did we race? Don't try to optimize this, please, it's not so likely
+ * to happen, and no more than once at the boot. */
+ if(write_sigio_pid != -1)
+ goto out_unlock;
+
+ write_sigio_pid = run_helper_thread(write_sigio_thread, NULL,
CLONE_FILES | CLONE_VM, &stack, 0);

- if(write_sigio_pid < 0) goto out_close2;
+ if (write_sigio_pid < 0)
+ goto out_clear;

- if(write_sigio_irq(write_sigio_fds[0]))
+ if (write_sigio_irq(l_write_sigio_fds[0]))
goto out_kill;

- out:
+ /* Success, finally. */
+ memcpy(write_sigio_fds, l_write_sigio_fds, sizeof(l_write_sigio_fds));
+ memcpy(sigio_private, l_sigio_private, sizeof(l_sigio_private));
+
+ current_poll = ((struct pollfds) { .poll = p,
+ .used = 1,
+ .size = 1 });
+
sigio_unlock();
return;

out_kill:
- os_kill_process(write_sigio_pid, 1);
+ l_write_sigio_pid = write_sigio_pid;
+ write_sigio_pid = -1;
+ sigio_unlock();
+ /* Going to call waitpid, avoid holding the lock. */
+ os_kill_process(l_write_sigio_pid, 1);
+ goto out_free;
+
+ out_clear:
write_sigio_pid = -1;
+ out_unlock:
+ sigio_unlock();
+ out_free:
+ kfree(p);
out_close2:
- os_close_file(sigio_private[0]);
- os_close_file(sigio_private[1]);
+ os_close_file(l_sigio_private[0]);
+ os_close_file(l_sigio_private[1]);
out_close1:
- os_close_file(write_sigio_fds[0]);
- os_close_file(write_sigio_fds[1]);
- sigio_unlock();
+ os_close_file(l_write_sigio_fds[0]);
+ os_close_file(l_write_sigio_fds[1]);
+ return;
+
}

int read_sigio_fd(int fd)


2005-10-25 22:05:59

by Blaisorblade

[permalink] [raw]
Subject: [PATCH 05/11] uml: fix assembly stub for segv

From: Paolo 'Blaisorblade' Giarrusso <[email protected]>

Even here, we reuse values from one asm statement to the next without telling
this to GCC - so fix this.

While at it, a bit of improvements to the generated asm code, with better use of
constraints. Still TODO: convert all this to the syscall_stub macros we already
have.

Signed-off-by: Paolo 'Blaisorblade' Giarrusso <[email protected]>
---

arch/um/sys-i386/stub_segv.c | 10 ++++++----
arch/um/sys-x86_64/stub_segv.c | 18 ++++++++++--------
2 files changed, 16 insertions(+), 12 deletions(-)

diff --git a/arch/um/sys-i386/stub_segv.c b/arch/um/sys-i386/stub_segv.c
--- a/arch/um/sys-i386/stub_segv.c
+++ b/arch/um/sys-i386/stub_segv.c
@@ -13,17 +13,19 @@ void __attribute__ ((__section__ (".__sy
stub_segv_handler(int sig)
{
struct sigcontext *sc = (struct sigcontext *) (&sig + 1);
+ long pid;

GET_FAULTINFO_FROM_SC(*((struct faultinfo *) UML_CONFIG_STUB_DATA),
sc);

- __asm__("movl %0, %%eax ; int $0x80": : "g" (__NR_getpid));
- __asm__("movl %%eax, %%ebx ; movl %0, %%eax ; movl %1, %%ecx ;"
- "int $0x80": : "g" (__NR_kill), "g" (SIGUSR1));
+ __asm__("movl %1, %%eax ; int $0x80": "=&a" (pid): "i" (__NR_getpid));
+ __asm__("movl %0, %%eax ; movl %1, %%ecx ;"
+ "int $0x80": : "i" (__NR_kill), "i" (SIGUSR1), "b" (pid)
+ : "eax", "ecx");
/* Load pointer to sigcontext into esp, since we need to leave
* the stack in its original form when we do the sigreturn here, by
* hand.
*/
__asm__("mov %0,%%esp ; movl %1, %%eax ; "
- "int $0x80" : : "a" (sc), "g" (__NR_sigreturn));
+ "int $0x80" : : "r" (sc), "i" (__NR_sigreturn));
}
diff --git a/arch/um/sys-x86_64/stub_segv.c b/arch/um/sys-x86_64/stub_segv.c
--- a/arch/um/sys-x86_64/stub_segv.c
+++ b/arch/um/sys-x86_64/stub_segv.c
@@ -31,21 +31,23 @@ void __attribute__ ((__section__ (".__sy
stub_segv_handler(int sig)
{
struct ucontext *uc;
+ long pid;

- __asm__("movq %%rdx, %0" : "=g" (uc) :);
+ __asm__("movq %%rdx, %0" : "=g" (uc) : );
GET_FAULTINFO_FROM_SC(*((struct faultinfo *) UML_CONFIG_STUB_DATA),
&uc->uc_mcontext);

- __asm__("movq %0, %%rax ; syscall": : "g" (__NR_getpid));
- __asm__("movq %%rax, %%rdi ; movq %0, %%rax ; movq %1, %%rsi ;"
- "syscall": : "g" (__NR_kill), "g" (SIGUSR1) :
- "%rdi", "%rax", "%rsi");
+ __asm__("movq %0, %%rax ; syscall": "=&a" (pid) : "g" (__NR_getpid)
+ : "rax", __syscall_clobber);
+ __asm__("movq %0, %%rax ; movq %1, %%rsi ;"
+ "syscall": : "i" (__NR_kill), "i" (SIGUSR1), "D" (pid) :
+ "rdi", "rax", "rsi", __syscall_clobber);
/* sys_sigreturn expects that the stack pointer will be 8 bytes into
* the signal frame. So, we use the ucontext pointer, which we know
* already, to get the signal frame pointer, and add 8 to that.
*/
- __asm__("movq %0, %%rsp": :
- "g" ((unsigned long) container_of(uc, struct rt_sigframe,
+ __asm__("movq %0, %%rsp": :
+ "g" ((unsigned long) container_of(uc, struct rt_sigframe,
uc) + 8));
- __asm__("movq %0, %%rax ; syscall" : : "g" (__NR_rt_sigreturn));
+ __asm__("movq %0, %%rax ; syscall" : : "g" (__NR_rt_sigreturn) : "rax");
}

2005-10-25 22:05:59

by Blaisorblade

[permalink] [raw]
Subject: [PATCH 03/11] uml: fix signal code x86-64 [for 2.6.15]

From: Paolo 'Blaisorblade' Giarrusso <[email protected]>

The problems in this area came to light while fixing a compile failure with
GCC 4, in commit bcb01b8a67476e6f748086e626df8424cc27036d. I went comparing this
code with x86_64 frame construction (which we should ABI compatible with) and
resync'ed the code a bit.

It isn't yet perfect, because we don't yet save floating point context. But that
will come later.

Please give a critical eye, even because things currently have no reported
misbehaviour, and this code is complex enough.

CC: Andi Kleen <[email protected]>
Signed-off-by: Paolo 'Blaisorblade' Giarrusso <[email protected]>
---

arch/um/sys-x86_64/signal.c | 19 ++++++++++++++++---
1 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/arch/um/sys-x86_64/signal.c b/arch/um/sys-x86_64/signal.c
--- a/arch/um/sys-x86_64/signal.c
+++ b/arch/um/sys-x86_64/signal.c
@@ -164,6 +164,7 @@ struct rt_sigframe

#define round_down(m, n) (((m) / (n)) * (n))

+/* Taken from arch/x86_64/kernel/signal.c:setup_rt_frame(). */
int setup_signal_stack_si(unsigned long stack_top, int sig,
struct k_sigaction *ka, struct pt_regs * regs,
siginfo_t *info, sigset_t *set)
@@ -173,9 +174,21 @@ int setup_signal_stack_si(unsigned long
int err = 0;
struct task_struct *me = current;

- frame = (struct rt_sigframe __user *)
- round_down(stack_top - sizeof(struct rt_sigframe), 16) - 8;
- frame = (struct rt_sigframe *) ((unsigned long) frame - 128);
+ /* Leave space on the stack for the Red Zone, and for saving FP
+ * registers, even if this doesn't happen. We don't have a way to test
+ * used_math(), so we do that inconditionally.
+ *
+ * XXX: RED-PEN: currently, we're using a Red Zone also for any
+ * alternate stack set up by sigaltstack(), which x86-64 doesn't do
+ * (because there shouldn't be any code executing there). This could
+ * cause failures if user setup a too little alternate stack.*/
+
+ fp = (struct rt_sigframe *) round_down(stack_top - 128 -
+ sizeof(struct _fpstate), 16);
+
+ /* Now leave the space for the rest of signal frame. */
+ frame = (void __user *) round_down((unsigned long) fp -
+ sizeof(struct rt_sigframe), 16) - 8;

if (!access_ok(VERIFY_WRITE, fp, sizeof(struct _fpstate)))
goto out;

2005-10-25 22:05:34

by Blaisorblade

[permalink] [raw]
Subject: [PATCH 09/11] uml console channels: fix the API of console_write

From: Paolo 'Blaisorblade' Giarrusso <[email protected]>

Since the 4th param is unused, remove it altogether.

Signed-off-by: Paolo 'Blaisorblade' Giarrusso <[email protected]>
---

arch/um/drivers/chan_kern.c | 5 ++---
arch/um/drivers/chan_user.c | 2 +-
arch/um/include/chan_user.h | 4 ++--
3 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/arch/um/drivers/chan_kern.c b/arch/um/drivers/chan_kern.c
--- a/arch/um/drivers/chan_kern.c
+++ b/arch/um/drivers/chan_kern.c
@@ -89,8 +89,7 @@ static int not_configged_write(int fd, c
return(-EIO);
}

-static int not_configged_console_write(int fd, const char *buf, int len,
- void *data)
+static int not_configged_console_write(int fd, const char *buf, int len)
{
my_puts("Using a channel type which is configured out of "
"UML\n");
@@ -299,7 +298,7 @@ int console_write_chan(struct list_head
chan = list_entry(ele, struct chan, list);
if(!chan->output || (chan->ops->console_write == NULL))
continue;
- n = chan->ops->console_write(chan->fd, buf, len, chan->data);
+ n = chan->ops->console_write(chan->fd, buf, len);
if(chan->primary) ret = n;
}
return(ret);
diff --git a/arch/um/drivers/chan_user.c b/arch/um/drivers/chan_user.c
--- a/arch/um/drivers/chan_user.c
+++ b/arch/um/drivers/chan_user.c
@@ -21,7 +21,7 @@
#include "choose-mode.h"
#include "mode.h"

-int generic_console_write(int fd, const char *buf, int n, void *unused)
+int generic_console_write(int fd, const char *buf, int n)
{
struct termios save, new;
int err;
diff --git a/arch/um/include/chan_user.h b/arch/um/include/chan_user.h
--- a/arch/um/include/chan_user.h
+++ b/arch/um/include/chan_user.h
@@ -25,7 +25,7 @@ struct chan_ops {
void (*close)(int, void *);
int (*read)(int, char *, void *);
int (*write)(int, const char *, int, void *);
- int (*console_write)(int, const char *, int, void *);
+ int (*console_write)(int, const char *, int);
int (*window_size)(int, void *, unsigned short *, unsigned short *);
void (*free)(void *);
int winch;
@@ -37,7 +37,7 @@ extern struct chan_ops fd_ops, null_ops,
extern void generic_close(int fd, void *unused);
extern int generic_read(int fd, char *c_out, void *unused);
extern int generic_write(int fd, const char *buf, int n, void *unused);
-extern int generic_console_write(int fd, const char *buf, int n, void *state);
+extern int generic_console_write(int fd, const char *buf, int n);
extern int generic_window_size(int fd, void *unused, unsigned short *rows_out,
unsigned short *cols_out);
extern void generic_free(void *data);

2005-10-25 22:06:41

by Blaisorblade

[permalink] [raw]
Subject: [PATCH 02/11] Uml: fix access_ok

From: Paolo 'Blaisorblade' Giarrusso <[email protected]>

The access_ok_tt() macro is bogus, in that a read access is unconditionally
considered valid.

I couldn't find in SCM logs the introduction of this check, but I went back to
2.4.20-1um and the definition was the same.

Possibly this was done to avoid problems with missing set_fs() calls, but there
can't be any I think because they would fail with SKAS mode. TT-specific code is
still to check.

Also, this patch joins common code together, and makes the "address range
wrapping" check happen for all cases, rather than for only some.

This may, possibly, be reoptimized at some time, but the current code doesn't
seem clever, just confused.

* Important: I've also had to change references to access_ok_{tt,skas} back to
access_ok - the kernel wasn't that happy otherwise.
Signed-off-by: Paolo 'Blaisorblade' Giarrusso <[email protected]>
---

arch/um/include/um_uaccess.h | 19 ++++++++++++++++++-
arch/um/kernel/skas/include/uaccess-skas.h | 10 ++--------
arch/um/kernel/skas/uaccess.c | 8 ++++----
arch/um/kernel/tt/include/uaccess-tt.h | 8 +-------
arch/um/kernel/tt/uaccess.c | 8 ++++----
5 files changed, 29 insertions(+), 24 deletions(-)

diff --git a/arch/um/include/um_uaccess.h b/arch/um/include/um_uaccess.h
--- a/arch/um/include/um_uaccess.h
+++ b/arch/um/include/um_uaccess.h
@@ -17,8 +17,25 @@
#include "uaccess-skas.h"
#endif

+#define __under_task_size(addr, size) \
+ (((unsigned long) (addr) < TASK_SIZE) && \
+ (((unsigned long) (addr) + (size)) < TASK_SIZE))
+
+#define __access_ok_vsyscall(type, addr, size) \
+ ((type == VERIFY_READ) && \
+ ((unsigned long) (addr) >= FIXADDR_USER_START) && \
+ ((unsigned long) (addr) + (size) <= FIXADDR_USER_END) && \
+ ((unsigned long) (addr) + (size) >= (unsigned long)(addr)))
+
+#define __addr_range_nowrap(addr, size) \
+ ((unsigned long) (addr) <= ((unsigned long) (addr) + (size)))
+
#define access_ok(type, addr, size) \
- CHOOSE_MODE_PROC(access_ok_tt, access_ok_skas, type, addr, size)
+ (__addr_range_nowrap(addr, size) && \
+ (__under_task_size(addr, size) || \
+ __access_ok_vsyscall(type, addr, size) || \
+ segment_eq(get_fs(), KERNEL_DS) || \
+ CHOOSE_MODE_PROC(access_ok_tt, access_ok_skas, type, addr, size)))

static inline int copy_from_user(void *to, const void __user *from, int n)
{
diff --git a/arch/um/kernel/skas/include/uaccess-skas.h b/arch/um/kernel/skas/include/uaccess-skas.h
--- a/arch/um/kernel/skas/include/uaccess-skas.h
+++ b/arch/um/kernel/skas/include/uaccess-skas.h
@@ -9,14 +9,8 @@
#include "asm/errno.h"
#include "asm/fixmap.h"

-#define access_ok_skas(type, addr, size) \
- ((segment_eq(get_fs(), KERNEL_DS)) || \
- (((unsigned long) (addr) < TASK_SIZE) && \
- ((unsigned long) (addr) + (size) <= TASK_SIZE)) || \
- ((type == VERIFY_READ ) && \
- ((unsigned long) (addr) >= FIXADDR_USER_START) && \
- ((unsigned long) (addr) + (size) <= FIXADDR_USER_END) && \
- ((unsigned long) (addr) + (size) >= (unsigned long)(addr))))
+/* No SKAS-specific checking. */
+#define access_ok_skas(type, addr, size) 0

extern int copy_from_user_skas(void *to, const void __user *from, int n);
extern int copy_to_user_skas(void __user *to, const void *from, int n);
diff --git a/arch/um/kernel/skas/uaccess.c b/arch/um/kernel/skas/uaccess.c
--- a/arch/um/kernel/skas/uaccess.c
+++ b/arch/um/kernel/skas/uaccess.c
@@ -143,7 +143,7 @@ int copy_from_user_skas(void *to, const
return(0);
}

- return(access_ok_skas(VERIFY_READ, from, n) ?
+ return(access_ok(VERIFY_READ, from, n) ?
buffer_op((unsigned long) from, n, 0, copy_chunk_from_user, &to):
n);
}
@@ -164,7 +164,7 @@ int copy_to_user_skas(void __user *to, c
return(0);
}

- return(access_ok_skas(VERIFY_WRITE, to, n) ?
+ return(access_ok(VERIFY_WRITE, to, n) ?
buffer_op((unsigned long) to, n, 1, copy_chunk_to_user, &from) :
n);
}
@@ -193,7 +193,7 @@ int strncpy_from_user_skas(char *dst, co
return(strnlen(dst, count));
}

- if(!access_ok_skas(VERIFY_READ, src, 1))
+ if(!access_ok(VERIFY_READ, src, 1))
return(-EFAULT);

n = buffer_op((unsigned long) src, count, 0, strncpy_chunk_from_user,
@@ -221,7 +221,7 @@ int clear_user_skas(void __user *mem, in
return(0);
}

- return(access_ok_skas(VERIFY_WRITE, mem, len) ?
+ return(access_ok(VERIFY_WRITE, mem, len) ?
buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL) : len);
}

diff --git a/arch/um/kernel/tt/include/uaccess-tt.h b/arch/um/kernel/tt/include/uaccess-tt.h
--- a/arch/um/kernel/tt/include/uaccess-tt.h
+++ b/arch/um/kernel/tt/include/uaccess-tt.h
@@ -19,19 +19,13 @@
extern unsigned long end_vm;
extern unsigned long uml_physmem;

-#define under_task_size(addr, size) \
- (((unsigned long) (addr) < TASK_SIZE) && \
- (((unsigned long) (addr) + (size)) < TASK_SIZE))
-
#define is_stack(addr, size) \
(((unsigned long) (addr) < STACK_TOP) && \
((unsigned long) (addr) >= STACK_TOP - ABOVE_KMEM) && \
(((unsigned long) (addr) + (size)) <= STACK_TOP))

#define access_ok_tt(type, addr, size) \
- ((type == VERIFY_READ) || (segment_eq(get_fs(), KERNEL_DS)) || \
- (((unsigned long) (addr) <= ((unsigned long) (addr) + (size))) && \
- (under_task_size(addr, size) || is_stack(addr, size))))
+ (is_stack(addr, size))

extern unsigned long get_fault_addr(void);

diff --git a/arch/um/kernel/tt/uaccess.c b/arch/um/kernel/tt/uaccess.c
--- a/arch/um/kernel/tt/uaccess.c
+++ b/arch/um/kernel/tt/uaccess.c
@@ -8,7 +8,7 @@

int copy_from_user_tt(void *to, const void __user *from, int n)
{
- if(!access_ok_tt(VERIFY_READ, from, n))
+ if(!access_ok(VERIFY_READ, from, n))
return(n);

return(__do_copy_from_user(to, from, n, &current->thread.fault_addr,
@@ -17,7 +17,7 @@ int copy_from_user_tt(void *to, const vo

int copy_to_user_tt(void __user *to, const void *from, int n)
{
- if(!access_ok_tt(VERIFY_WRITE, to, n))
+ if(!access_ok(VERIFY_WRITE, to, n))
return(n);

return(__do_copy_to_user(to, from, n, &current->thread.fault_addr,
@@ -28,7 +28,7 @@ int strncpy_from_user_tt(char *dst, cons
{
int n;

- if(!access_ok_tt(VERIFY_READ, src, 1))
+ if(!access_ok(VERIFY_READ, src, 1))
return(-EFAULT);

n = __do_strncpy_from_user(dst, src, count,
@@ -47,7 +47,7 @@ int __clear_user_tt(void __user *mem, in

int clear_user_tt(void __user *mem, int len)
{
- if(!access_ok_tt(VERIFY_WRITE, mem, len))
+ if(!access_ok(VERIFY_WRITE, mem, len))
return(len);

return(__do_clear_user(mem, len, &current->thread.fault_addr,

2005-10-25 22:07:21

by Blaisorblade

[permalink] [raw]
Subject: [PATCH 10/11] uml: avoid malloc to sleep in atomic sections

From: Paolo 'Blaisorblade' Giarrusso <[email protected]>

Ugly trick to help make malloc not sleeping - we can't do anything else. But
this is not yet optimal, since spinlock don't trigger in_atomic() when
preemption is disabled.

Also, even if ugly, this was already used in one place, and was even more bogus.
Fix it.

Signed-off-by: Paolo 'Blaisorblade' Giarrusso <[email protected]>
---

arch/um/include/kern_util.h | 4 +++-
arch/um/include/user.h | 1 +
arch/um/kernel/helper.c | 4 ++--
arch/um/kernel/main.c | 11 +++++++----
arch/um/kernel/process_kern.c | 21 +++++++++++++--------
5 files changed, 26 insertions(+), 15 deletions(-)

diff --git a/arch/um/include/kern_util.h b/arch/um/include/kern_util.h
--- a/arch/um/include/kern_util.h
+++ b/arch/um/include/kern_util.h
@@ -109,9 +109,11 @@ extern void machine_halt(void);
extern int is_syscall(unsigned long addr);
extern void arch_switch(void);
extern void free_irq(unsigned int, void *);
-extern int um_in_interrupt(void);
extern int cpu(void);

+/* Are we disallowed to sleep? Used to choose between GFP_KERNEL and GFP_ATOMIC. */
+extern int __cant_sleep(void);
+
#endif

/*
diff --git a/arch/um/include/user.h b/arch/um/include/user.h
--- a/arch/um/include/user.h
+++ b/arch/um/include/user.h
@@ -18,6 +18,7 @@ extern int open_gdb_chan(void);
extern unsigned long strlcpy(char *, const char *, unsigned long);
extern unsigned long strlcat(char *, const char *, unsigned long);
extern void *um_vmalloc(int size);
+extern void *um_vmalloc_atomic(int size);
extern void vfree(void *ptr);

#endif
diff --git a/arch/um/kernel/helper.c b/arch/um/kernel/helper.c
--- a/arch/um/kernel/helper.c
+++ b/arch/um/kernel/helper.c
@@ -61,7 +61,7 @@ int run_helper(void (*pre_exec)(void *),

if((stack_out != NULL) && (*stack_out != 0))
stack = *stack_out;
- else stack = alloc_stack(0, um_in_interrupt());
+ else stack = alloc_stack(0, __cant_sleep());
if(stack == 0)
return(-ENOMEM);

@@ -124,7 +124,7 @@ int run_helper_thread(int (*proc)(void *
unsigned long stack, sp;
int pid, status, err;

- stack = alloc_stack(stack_order, um_in_interrupt());
+ stack = alloc_stack(stack_order, __cant_sleep());
if(stack == 0) return(-ENOMEM);

sp = stack + (page_size() << stack_order) - sizeof(void *);
diff --git a/arch/um/kernel/main.c b/arch/um/kernel/main.c
--- a/arch/um/kernel/main.c
+++ b/arch/um/kernel/main.c
@@ -197,11 +197,14 @@ void *__wrap_malloc(int size)
{
void *ret;

- if(!CAN_KMALLOC())
+ if (!CAN_KMALLOC())
return(__real_malloc(size));
- else if(size <= PAGE_SIZE) /* finding contiguos pages can be hard*/
- ret = um_kmalloc(size);
- else ret = um_vmalloc(size);
+ else if (size <= PAGE_SIZE) /* finding contiguos pages can be hard*/
+ ret = __cant_sleep() ? um_kmalloc_atomic(size) :
+ um_kmalloc(size);
+ else
+ ret = __cant_sleep() ? um_vmalloc_atomic(size) :
+ um_vmalloc(size);

/* glibc people insist that if malloc fails, errno should be
* set by malloc as well. So we do.
diff --git a/arch/um/kernel/process_kern.c b/arch/um/kernel/process_kern.c
--- a/arch/um/kernel/process_kern.c
+++ b/arch/um/kernel/process_kern.c
@@ -287,17 +287,27 @@ EXPORT_SYMBOL(disable_hlt);

void *um_kmalloc(int size)
{
- return(kmalloc(size, GFP_KERNEL));
+ return kmalloc(size, GFP_KERNEL);
}

void *um_kmalloc_atomic(int size)
{
- return(kmalloc(size, GFP_ATOMIC));
+ return kmalloc(size, GFP_ATOMIC);
}

void *um_vmalloc(int size)
{
- return(vmalloc(size));
+ return vmalloc(size);
+}
+
+void *um_vmalloc_atomic(int size)
+{
+ return __vmalloc(size, GFP_ATOMIC | __GFP_HIGHMEM, PAGE_KERNEL);
+}
+
+int __cant_sleep(void) {
+ return in_atomic() || irqs_disabled() || in_interrupt();
+ /* Is in_interrupt() really needed? */
}

unsigned long get_fault_addr(void)
@@ -373,11 +383,6 @@ int smp_sigio_handler(void)
return(0);
}

-int um_in_interrupt(void)
-{
- return(in_interrupt());
-}
-
int cpu(void)
{
return(current_thread->cpu);

2005-10-25 22:06:41

by Blaisorblade

[permalink] [raw]
Subject: [PATCH 08/11] uml console channels: remove console_write wrappers

From: Paolo 'Blaisorblade' Giarrusso <[email protected]>

We were using a long series of (stupid) wrappers which all call
generic_console_write(). Since the wrappers only change the 4th param, which is
unused by the called proc, remove them and call generic_console_write()
directly.

If needed at any time in the future to reintroduce this stuff, the member could
be moved to a generic struct, to avoid this duplicated handling.

Signed-off-by: Paolo 'Blaisorblade' Giarrusso <[email protected]>
---

arch/um/drivers/fd.c | 9 +--------
arch/um/drivers/port_user.c | 9 +--------
arch/um/drivers/pty.c | 11 ++---------
arch/um/drivers/tty.c | 9 +--------
arch/um/drivers/xterm.c | 9 +--------
5 files changed, 6 insertions(+), 41 deletions(-)

diff --git a/arch/um/drivers/fd.c b/arch/um/drivers/fd.c
--- a/arch/um/drivers/fd.c
+++ b/arch/um/drivers/fd.c
@@ -76,13 +76,6 @@ static void fd_close(int fd, void *d)
}
}

-static int fd_console_write(int fd, const char *buf, int n, void *d)
-{
- struct fd_chan *data = d;
-
- return(generic_console_write(fd, buf, n, &data->tt));
-}
-
struct chan_ops fd_ops = {
.type = "fd",
.init = fd_init,
@@ -90,7 +83,7 @@ struct chan_ops fd_ops = {
.close = fd_close,
.read = generic_read,
.write = generic_write,
- .console_write = fd_console_write,
+ .console_write = generic_console_write,
.window_size = generic_window_size,
.free = generic_free,
.winch = 1,
diff --git a/arch/um/drivers/port_user.c b/arch/um/drivers/port_user.c
--- a/arch/um/drivers/port_user.c
+++ b/arch/um/drivers/port_user.c
@@ -101,13 +101,6 @@ static void port_close(int fd, void *d)
os_close_file(fd);
}

-static int port_console_write(int fd, const char *buf, int n, void *d)
-{
- struct port_chan *data = d;
-
- return(generic_console_write(fd, buf, n, &data->tt));
-}
-
struct chan_ops port_ops = {
.type = "port",
.init = port_init,
@@ -115,7 +108,7 @@ struct chan_ops port_ops = {
.close = port_close,
.read = generic_read,
.write = generic_write,
- .console_write = port_console_write,
+ .console_write = generic_console_write,
.window_size = generic_window_size,
.free = port_free,
.winch = 1,
diff --git a/arch/um/drivers/pty.c b/arch/um/drivers/pty.c
--- a/arch/um/drivers/pty.c
+++ b/arch/um/drivers/pty.c
@@ -118,13 +118,6 @@ static int pty_open(int input, int outpu
return(fd);
}

-static int pty_console_write(int fd, const char *buf, int n, void *d)
-{
- struct pty_chan *data = d;
-
- return(generic_console_write(fd, buf, n, &data->tt));
-}
-
struct chan_ops pty_ops = {
.type = "pty",
.init = pty_chan_init,
@@ -132,7 +125,7 @@ struct chan_ops pty_ops = {
.close = generic_close,
.read = generic_read,
.write = generic_write,
- .console_write = pty_console_write,
+ .console_write = generic_console_write,
.window_size = generic_window_size,
.free = generic_free,
.winch = 0,
@@ -145,7 +138,7 @@ struct chan_ops pts_ops = {
.close = generic_close,
.read = generic_read,
.write = generic_write,
- .console_write = pty_console_write,
+ .console_write = generic_console_write,
.window_size = generic_window_size,
.free = generic_free,
.winch = 0,
diff --git a/arch/um/drivers/tty.c b/arch/um/drivers/tty.c
--- a/arch/um/drivers/tty.c
+++ b/arch/um/drivers/tty.c
@@ -60,13 +60,6 @@ static int tty_open(int input, int outpu
return(fd);
}

-static int tty_console_write(int fd, const char *buf, int n, void *d)
-{
- struct tty_chan *data = d;
-
- return(generic_console_write(fd, buf, n, &data->tt));
-}
-
struct chan_ops tty_ops = {
.type = "tty",
.init = tty_chan_init,
@@ -74,7 +67,7 @@ struct chan_ops tty_ops = {
.close = generic_close,
.read = generic_read,
.write = generic_write,
- .console_write = tty_console_write,
+ .console_write = generic_console_write,
.window_size = generic_window_size,
.free = generic_free,
.winch = 0,
diff --git a/arch/um/drivers/xterm.c b/arch/um/drivers/xterm.c
--- a/arch/um/drivers/xterm.c
+++ b/arch/um/drivers/xterm.c
@@ -195,13 +195,6 @@ static void xterm_free(void *d)
free(d);
}

-static int xterm_console_write(int fd, const char *buf, int n, void *d)
-{
- struct xterm_chan *data = d;
-
- return(generic_console_write(fd, buf, n, &data->tt));
-}
-
struct chan_ops xterm_ops = {
.type = "xterm",
.init = xterm_init,
@@ -209,7 +202,7 @@ struct chan_ops xterm_ops = {
.close = xterm_close,
.read = generic_read,
.write = generic_write,
- .console_write = xterm_console_write,
+ .console_write = generic_console_write,
.window_size = generic_window_size,
.free = xterm_free,
.winch = 1,

2005-10-25 22:07:22

by Blaisorblade

[permalink] [raw]
Subject: [PATCH 11/11] uml: micro fixups to arch Kconfig

From: Paolo 'Blaisorblade' Giarrusso <[email protected]>

Remove a stone-age comment (UM *does* have a MMU, i.e. the host), and fix a
dependency (introduced in commit 02edeb586ae4cdd17778923674700edb732a4741) to
do what was intended.

Signed-off-by: Paolo 'Blaisorblade' Giarrusso <[email protected]>
---

arch/um/Kconfig | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/um/Kconfig b/arch/um/Kconfig
--- a/arch/um/Kconfig
+++ b/arch/um/Kconfig
@@ -7,7 +7,6 @@ config UML
bool
default y

-# XXX: does UM have a mmu/swap?
config MMU
bool
default y
@@ -196,7 +195,8 @@ config HOST_2G_2G
config SMP
bool "Symmetric multi-processing support (EXPERIMENTAL)"
default n
- depends on (MODE_TT && EXPERIMENTAL && !SMP_BROKEN) || (BROKEN && SMP_BROKEN)
+ #SMP_BROKEN is for x86_64.
+ depends on MODE_TT && EXPERIMENTAL && (!SMP_BROKEN || (BROKEN && SMP_BROKEN))
help
This option enables UML SMP support.
It is NOT related to having a real SMP box. Not directly, at least.

2005-10-25 22:08:49

by Blaisorblade

[permalink] [raw]
Subject: [PATCH 04/11] uml: fix SKAS0 assembly stubs - use proper constraints

From: Paolo 'Blaisorblade' Giarrusso <[email protected]>

Jeff Dike noted that the assembly code for syscall stubs is misassembled with
GCC 3.2.3: the values copied in registers weren't preserved between one asm()
and the following one.

So I fixed the thing by rewriting the __asm__ constraints more
like unistd.h ones.

Note: in syscall6 case I had to add one more instruction (i.e. moving arg6 in
eax and shuffling things around) - it's needed for the function to be valid in
general (we can't load the value from the stack, relative to ebp, because we
change it), but could be avoided since we actually use a constant as param 6.

The only fix would be to turn stub_syscall6 to a macro and use a "i" constraint
for arg6 (i.e., specify it's a constant value).

Signed-off-by: Paolo 'Blaisorblade' Giarrusso <[email protected]>
---

arch/um/include/sysdep-i386/stub.h | 64 +++++++++++++++++++++++-----------
arch/um/include/sysdep-x86_64/stub.h | 61 ++++++++++++++++++++++++++------
2 files changed, 92 insertions(+), 33 deletions(-)

diff --git a/arch/um/include/sysdep-i386/stub.h b/arch/um/include/sysdep-i386/stub.h
--- a/arch/um/include/sysdep-i386/stub.h
+++ b/arch/um/include/sysdep-i386/stub.h
@@ -16,45 +16,69 @@ extern void stub_clone_handler(void);
#define STUB_MMAP_NR __NR_mmap2
#define MMAP_OFFSET(o) ((o) >> PAGE_SHIFT)

+static inline long stub_syscall1(long syscall, long arg1)
+{
+ long ret;
+
+ __asm__ volatile ("int $0x80" : "=a" (ret) : "0" (syscall), "b" (arg1));
+
+ return ret;
+}
+
static inline long stub_syscall2(long syscall, long arg1, long arg2)
{
long ret;

- __asm__("movl %0, %%ecx; " : : "g" (arg2) : "%ecx");
- __asm__("movl %0, %%ebx; " : : "g" (arg1) : "%ebx");
- __asm__("movl %0, %%eax; " : : "g" (syscall) : "%eax");
- __asm__("int $0x80;" : : : "%eax");
- __asm__ __volatile__("movl %%eax, %0; " : "=g" (ret) :);
- return(ret);
+ __asm__ volatile ("int $0x80" : "=a" (ret) : "0" (syscall), "b" (arg1),
+ "c" (arg2));
+
+ return ret;
}

static inline long stub_syscall3(long syscall, long arg1, long arg2, long arg3)
{
- __asm__("movl %0, %%edx; " : : "g" (arg3) : "%edx");
- return(stub_syscall2(syscall, arg1, arg2));
+ long ret;
+
+ __asm__ volatile ("int $0x80" : "=a" (ret) : "0" (syscall), "b" (arg1),
+ "c" (arg2), "d" (arg3));
+
+ return ret;
}

static inline long stub_syscall4(long syscall, long arg1, long arg2, long arg3,
long arg4)
{
- __asm__("movl %0, %%esi; " : : "g" (arg4) : "%esi");
- return(stub_syscall3(syscall, arg1, arg2, arg3));
+ long ret;
+
+ __asm__ volatile ("int $0x80" : "=a" (ret) : "0" (syscall), "b" (arg1),
+ "c" (arg2), "d" (arg3), "S" (arg4));
+
+ return ret;
+}
+
+static inline long stub_syscall5(long syscall, long arg1, long arg2, long arg3,
+ long arg4, long arg5)
+{
+ long ret;
+
+ __asm__ volatile ("int $0x80" : "=a" (ret) : "0" (syscall), "b" (arg1),
+ "c" (arg2), "d" (arg3), "S" (arg4), "D" (arg5));
+
+ return ret;
}

static inline long stub_syscall6(long syscall, long arg1, long arg2, long arg3,
long arg4, long arg5, long arg6)
{
long ret;
- __asm__("movl %0, %%eax; " : : "g" (syscall) : "%eax");
- __asm__("movl %0, %%ebx; " : : "g" (arg1) : "%ebx");
- __asm__("movl %0, %%ecx; " : : "g" (arg2) : "%ecx");
- __asm__("movl %0, %%edx; " : : "g" (arg3) : "%edx");
- __asm__("movl %0, %%esi; " : : "g" (arg4) : "%esi");
- __asm__("movl %0, %%edi; " : : "g" (arg5) : "%edi");
- __asm__ __volatile__("pushl %%ebp ; movl %1, %%ebp; "
- "int $0x80; popl %%ebp ; "
- "movl %%eax, %0; " : "=g" (ret) : "g" (arg6) : "%eax");
- return(ret);
+
+ __asm__ volatile ("push %%ebp ; movl %%eax,%%ebp ; movl %1,%%eax ; "
+ "int $0x80 ; pop %%ebp"
+ : "=a" (ret)
+ : "g" (syscall), "b" (arg1), "c" (arg2), "d" (arg3),
+ "S" (arg4), "D" (arg5), "0" (arg6));
+
+ return ret;
}

static inline void trap_myself(void)
diff --git a/arch/um/include/sysdep-x86_64/stub.h b/arch/um/include/sysdep-x86_64/stub.h
--- a/arch/um/include/sysdep-x86_64/stub.h
+++ b/arch/um/include/sysdep-x86_64/stub.h
@@ -17,37 +17,72 @@ extern void stub_clone_handler(void);
#define STUB_MMAP_NR __NR_mmap
#define MMAP_OFFSET(o) (o)

+#define __syscall_clobber "r11","rcx","memory"
+#define __syscall "syscall"
+
static inline long stub_syscall2(long syscall, long arg1, long arg2)
{
long ret;

- __asm__("movq %0, %%rsi; " : : "g" (arg2) : "%rsi");
- __asm__("movq %0, %%rdi; " : : "g" (arg1) : "%rdi");
- __asm__("movq %0, %%rax; " : : "g" (syscall) : "%rax");
- __asm__("syscall;" : : : "%rax", "%r11", "%rcx");
- __asm__ __volatile__("movq %%rax, %0; " : "=g" (ret) :);
- return(ret);
+ __asm__ volatile (__syscall
+ : "=a" (ret)
+ : "0" (syscall), "D" (arg1), "S" (arg2) : __syscall_clobber );
+
+ return ret;
}

static inline long stub_syscall3(long syscall, long arg1, long arg2, long arg3)
{
- __asm__("movq %0, %%rdx; " : : "g" (arg3) : "%rdx");
- return(stub_syscall2(syscall, arg1, arg2));
+ long ret;
+
+ __asm__ volatile (__syscall
+ : "=a" (ret)
+ : "0" (syscall), "D" (arg1), "S" (arg2), "d" (arg3)
+ : __syscall_clobber );
+
+ return ret;
}

static inline long stub_syscall4(long syscall, long arg1, long arg2, long arg3,
long arg4)
{
- __asm__("movq %0, %%r10; " : : "g" (arg4) : "%r10");
- return(stub_syscall3(syscall, arg1, arg2, arg3));
+ long ret;
+
+ __asm__ volatile ("movq %5,%%r10 ; " __syscall
+ : "=a" (ret)
+ : "0" (syscall), "D" (arg1), "S" (arg2), "d" (arg3),
+ "g" (arg4)
+ : __syscall_clobber, "r10" );
+
+ return ret;
+}
+
+static inline long stub_syscall5(long syscall, long arg1, long arg2, long arg3,
+ long arg4, long arg5)
+{
+ long ret;
+
+ __asm__ volatile ("movq %5,%%r10 ; movq %6,%%r8 ; " __syscall
+ : "=a" (ret)
+ : "0" (syscall), "D" (arg1), "S" (arg2), "d" (arg3),
+ "g" (arg4), "g" (arg5)
+ : __syscall_clobber, "r10", "r8" );
+
+ return ret;
}

static inline long stub_syscall6(long syscall, long arg1, long arg2, long arg3,
long arg4, long arg5, long arg6)
{
- __asm__("movq %0, %%r9; " : : "g" (arg6) : "%r9");
- __asm__("movq %0, %%r8; " : : "g" (arg5) : "%r8");
- return(stub_syscall4(syscall, arg1, arg2, arg3, arg4));
+ long ret;
+
+ __asm__ volatile ("movq %5,%%r10 ; movq %6,%%r8 ; "
+ "movq %7, %%r9; " __syscall : "=a" (ret)
+ : "0" (syscall), "D" (arg1), "S" (arg2), "d" (arg3),
+ "g" (arg4), "g" (arg5), "g" (arg6)
+ : __syscall_clobber, "r10", "r8", "r9" );
+
+ return ret;
}

static inline void trap_myself(void)

2005-10-25 22:08:08

by Blaisorblade

[permalink] [raw]
Subject: [PATCH 06/11] uml: remove bogus WARN_ON, triggerable harmlessly on a page fault race

From: Paolo 'Blaisorblade' Giarrusso <[email protected]>

The below warning was added in place of pte_mkyoung(); if (is_write)
pte_mkdirty();

In fact, if the PTE is not marked young/dirty, our dirty/accessed bit emulation
would cause the TLB permission not to be changed, and so we'd loop, and given we
don't support preemption yet, we'd busy-hang here.

However, I've seen this warning trigger without crashes during a loop of
concurrent kernel builds, at random times (i.e. like a race condition), and I
realized that two concurrent faults on the same page, one on read and one on
write, can trigger it. The read fault gets serviced and the PTE gets marked
writable but clean (it's possible on a shared-writable mapping), while the
generic code sees the PTE was already installed and returns without action. In
this case, we'll see another fault and service it normally.

Signed-off-by: Paolo 'Blaisorblade' Giarrusso <[email protected]>
---

arch/um/kernel/trap_kern.c | 9 +++++++++
1 files changed, 9 insertions(+), 0 deletions(-)

diff --git a/arch/um/kernel/trap_kern.c b/arch/um/kernel/trap_kern.c
--- a/arch/um/kernel/trap_kern.c
+++ b/arch/um/kernel/trap_kern.c
@@ -95,7 +95,16 @@ survive:
pte = pte_offset_kernel(pmd, address);
} while(!pte_present(*pte));
err = 0;
+ /* The below warning was added in place of
+ * pte_mkyoung(); if (is_write) pte_mkdirty();
+ * If it's triggered, we'd see normally a hang here (a clean pte is
+ * marked read-only to emulate the dirty bit).
+ * However, the generic code can mark a PTE writable but clean on a
+ * concurrent read fault, triggering this harmlessly. So comment it out.
+ */
+#if 0
WARN_ON(!pte_young(*pte) || (is_write && !pte_dirty(*pte)));
+#endif
flush_tlb_page(vma, address);
out:
up_read(&mm->mmap_sem);

2005-10-25 22:08:08

by Blaisorblade

[permalink] [raw]
Subject: [PATCH 07/11] uml: fix mcast network driver error handling

From: Paolo 'Blaisorblade' Giarrusso <[email protected]>

printk clears the host errno (I verified this in debugging and it's reasonable
enough, given that it ends via a write call on some fd, especially since
printk() goes on /dev/tty0 which is often the host stdout).
So save errno earlier. There's no reason to change the printk calls to use -err
rather than errno - the assignment can't clear errno.

And in the first failure path, we used to return 0 too (and this time more
clearly), which is totally wrong. 0 is a success fd, which is then registered
and gives a "registering fd twice" warning.

Finally, fix up some whitespace.

Signed-off-by: Paolo 'Blaisorblade' Giarrusso <[email protected]>
---

arch/um/drivers/mcast_user.c | 20 ++++++++++----------
1 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/arch/um/drivers/mcast_user.c b/arch/um/drivers/mcast_user.c
--- a/arch/um/drivers/mcast_user.c
+++ b/arch/um/drivers/mcast_user.c
@@ -54,7 +54,7 @@ static int mcast_open(void *data)
struct mcast_data *pri = data;
struct sockaddr_in *sin = pri->mcast_addr;
struct ip_mreq mreq;
- int fd, yes = 1, err = 0;
+ int fd, yes = 1, err = -EINVAL;


if ((sin->sin_addr.s_addr == 0) || (sin->sin_port == 0))
@@ -63,40 +63,40 @@ static int mcast_open(void *data)
fd = socket(AF_INET, SOCK_DGRAM, 0);

if (fd < 0){
+ err = -errno;
printk("mcast_open : data socket failed, errno = %d\n",
errno);
- err = -errno;
goto out;
}

if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
+ err = -errno;
printk("mcast_open: SO_REUSEADDR failed, errno = %d\n",
errno);
- err = -errno;
goto out_close;
}

/* set ttl according to config */
if (setsockopt(fd, SOL_IP, IP_MULTICAST_TTL, &pri->ttl,
sizeof(pri->ttl)) < 0) {
+ err = -errno;
printk("mcast_open: IP_MULTICAST_TTL failed, error = %d\n",
errno);
- err = -errno;
goto out_close;
}

/* set LOOP, so data does get fed back to local sockets */
if (setsockopt(fd, SOL_IP, IP_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) {
+ err = -errno;
printk("mcast_open: IP_MULTICAST_LOOP failed, error = %d\n",
errno);
- err = -errno;
goto out_close;
}

/* bind socket to mcast address */
if (bind(fd, (struct sockaddr *) sin, sizeof(*sin)) < 0) {
- printk("mcast_open : data bind failed, errno = %d\n", errno);
err = -errno;
+ printk("mcast_open : data bind failed, errno = %d\n", errno);
goto out_close;
}

@@ -105,22 +105,22 @@ static int mcast_open(void *data)
mreq.imr_interface.s_addr = 0;
if (setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP,
&mreq, sizeof(mreq)) < 0) {
+ err = -errno;
printk("mcast_open: IP_ADD_MEMBERSHIP failed, error = %d\n",
errno);
printk("There appears not to be a multicast-capable network "
"interface on the host.\n");
printk("eth0 should be configured in order to use the "
"multicast transport.\n");
- err = -errno;
- goto out_close;
+ goto out_close;
}

return fd;

out_close:
- os_close_file(fd);
+ os_close_file(fd);
out:
- return err;
+ return err;
}

static void mcast_close(int fd, void *data)

2005-10-26 00:50:08

by Blaisorblade

[permalink] [raw]
Subject: Re: [PATCH 01/11] uml: sigio code - reduce spinlock hold time

On Wednesday 26 October 2005 02:17, Jesper Juhl wrote:
> On 10/26/05, Paolo 'Blaisorblade' Giarrusso <[email protected]> wrote:
> [snip]
>
> > And fix whitespace, at least for things I was touching anyway.
>
> [snip]
>
> > + return;
> > +
> > }

> Why an extra blank line at the end of the function?
Forgot to remove that blank line, sorry.

Jeff, remove when merging in your tree if you don't forget.
--
Inform me of my mistakes, so I can keep imitating Homer Simpson's "Doh!".
Paolo Giarrusso, aka Blaisorblade (Skype ID "PaoloGiarrusso", ICQ 215621894)
http://www.user-mode-linux.org/~blaisorblade





___________________________________
Yahoo! Mail: gratis 1GB per i messaggi e allegati da 10MB
http://mail.yahoo.it