From: Paolo 'Blaisorblade' Giarrusso <[EMAIL PROTECTED]> sigio_lock is taken both from process context and from interrupt context. So we *must* use irqsave.
Then, remove irq disabling from update_thread(), as it's called with sigio_lock() held (yes, set_signals(0) is local_irq_save). In fact, I've seen this causing frequent hangs with spinlock debugging enabled (I've verified well that the cause was an interrupt causing re-acquiring of this lock); however, now it's causing hangs as interrupt disabling causes some sleep-inside-spinlock checks to trigger - and then printk deadlocks. I've tested this for a long time and it is stable. I've also verified that nothing can sleep within this lock; to this purpose, I've had to verify everything inside um_request_irq; since it calls again write_sigio_workaround(), I've had to make atomic the allocation inside setup_initial_poll. HOWEVER, request_irq() can sleep, and in write_sigio_irq() thanks to IRQF_SAMPLE_RANDOM it _does_ sleep. So a separate patch makes write_sigio_irq() be called outside of sigio_lock(). Actually, I'm also quite dubious that an interrupt caused by other interrupts is a reliable entropy source, but that is another thing completely. Signed-off-by: Paolo 'Blaisorblade' Giarrusso <[EMAIL PROTECTED]> --- arch/um/include/sigio.h | 4 ++-- arch/um/kernel/sigio.c | 12 ++++++++---- arch/um/os-Linux/sigio.c | 36 ++++++++++++++++++++---------------- 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/arch/um/include/sigio.h b/arch/um/include/sigio.h index 434f1a9..a58bc1d 100644 --- a/arch/um/include/sigio.h +++ b/arch/um/include/sigio.h @@ -8,7 +8,7 @@ extern int write_sigio_irq(int fd); extern int register_sigio_fd(int fd); -extern void sigio_lock(void); -extern void sigio_unlock(void); +extern unsigned long sigio_lock(void); +extern void sigio_unlock(unsigned long flags); #endif diff --git a/arch/um/kernel/sigio.c b/arch/um/kernel/sigio.c index 89f9866..cc54db7 100644 --- a/arch/um/kernel/sigio.c +++ b/arch/um/kernel/sigio.c @@ -45,12 +45,16 @@ int write_sigio_irq(int fd) /* These are called from os-Linux/sigio.c to protect its pollfds arrays. */ static DEFINE_SPINLOCK(sigio_spinlock); -void sigio_lock(void) +unsigned long sigio_lock(void) { - spin_lock(&sigio_spinlock); + unsigned long flags; + + spin_lock_irqsave(&sigio_spinlock, flags); + + return flags; } -void sigio_unlock(void) +void sigio_unlock(unsigned long flags) { - spin_unlock(&sigio_spinlock); + spin_unlock_irqrestore(&sigio_spinlock, flags); } diff --git a/arch/um/os-Linux/sigio.c b/arch/um/os-Linux/sigio.c index 3fc43b3..88988fb 100644 --- a/arch/um/os-Linux/sigio.c +++ b/arch/um/os-Linux/sigio.c @@ -21,6 +21,10 @@ #include "os.h" #include "um_malloc.h" +/* Nothing in this file can sleep. I've verified each and every function. The + * only "exception" is write_sigio_thread, which runs in a host thread, so it + * has no chance of sleeping. */ + /* Protected by sigio_lock(), also used by sigio_cleanup, which is an * exitcall. */ @@ -121,11 +125,9 @@ static int need_poll(struct pollfds *polls, int n) */ static void update_thread(void) { - unsigned long flags; int n; char c; - flags = set_signals(0); n = os_write_file(sigio_private[0], &c, sizeof(c)); if(n != sizeof(c)){ printk("update_thread : write failed, err = %d\n", -n); @@ -138,7 +140,6 @@ static void update_thread(void) goto fail; } - set_signals(flags); return; fail: /* Critical section start */ @@ -150,15 +151,15 @@ static void update_thread(void) close(write_sigio_fds[0]); close(write_sigio_fds[1]); /* Critical section end */ - set_signals(flags); } int add_sigio_fd(int fd) { struct pollfd *p; int err = 0, i, n; + unsigned long flags; - sigio_lock(); + flags = sigio_lock(); for(i = 0; i < all_sigio_fds.used; i++){ if(all_sigio_fds.poll[i].fd == fd) break; @@ -184,7 +185,7 @@ int add_sigio_fd(int fd) next_poll.used = n + 1; update_thread(); out: - sigio_unlock(); + sigio_unlock(flags); return err; } @@ -192,6 +193,7 @@ int ignore_sigio_fd(int fd) { struct pollfd *p; int err = 0, i, n = 0; + unsigned long flags; /* This is called from exitcalls elsewhere in UML - if * sigio_cleanup has already run, then update_thread will hang @@ -200,7 +202,7 @@ int ignore_sigio_fd(int fd) if(write_sigio_pid == -1) return -EIO; - sigio_lock(); + flags = sigio_lock(); for(i = 0; i < current_poll.used; i++){ if(current_poll.poll[i].fd == fd) break; } @@ -220,7 +222,7 @@ int ignore_sigio_fd(int fd) update_thread(); out: - sigio_unlock(); + sigio_unlock(flags); return err; } @@ -228,7 +230,7 @@ static struct pollfd *setup_initial_poll(int fd) { struct pollfd *p; - p = um_kmalloc(sizeof(struct pollfd)); + p = um_kmalloc_atomic(sizeof(struct pollfd)); if (p == NULL) { printk("setup_initial_poll : failed to allocate poll\n"); return NULL; @@ -247,11 +249,12 @@ static void write_sigio_workaround(void) int l_write_sigio_fds[2]; int l_sigio_private[2]; int l_write_sigio_pid; + unsigned long flags; /* We call this *tons* of times - and most ones we must just fail. */ - sigio_lock(); + flags = sigio_lock(); l_write_sigio_pid = write_sigio_pid; - sigio_unlock(); + sigio_unlock(flags); if (l_write_sigio_pid != -1) return; @@ -273,7 +276,7 @@ static void write_sigio_workaround(void) if(!p) goto out_close2; - sigio_lock(); + flags = 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. */ @@ -296,7 +299,7 @@ static void write_sigio_workaround(void) if (write_sigio_pid < 0) goto out_clear; - sigio_unlock(); + sigio_unlock(flags); return; out_clear: @@ -310,7 +313,7 @@ out_clear_poll: .size = 0, .used = 0 }); out_free: - sigio_unlock(); + sigio_unlock(flags); kfree(p); out_close2: close(l_sigio_private[0]); @@ -323,6 +326,7 @@ out_close1: void maybe_sigio_broken(int fd, int read) { int err; + unsigned long flags; if(!isatty(fd)) return; @@ -332,7 +336,7 @@ void maybe_sigio_broken(int fd, int read) write_sigio_workaround(); - sigio_lock(); + flags = sigio_lock(); err = need_poll(&all_sigio_fds, all_sigio_fds.used + 1); if(err){ printk("maybe_sigio_broken - failed to add pollfd for " @@ -345,7 +349,7 @@ void maybe_sigio_broken(int fd, int read) .events = read ? POLLIN : POLLOUT, .revents = 0 }); out: - sigio_unlock(); + sigio_unlock(flags); } static void sigio_cleanup(void) - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/