From f75b1b1bedfb498cc43a992ce4d7ed8df3b1e770 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Sat, 17 Aug 2013 18:46:00 +0200 Subject: um: Implement probe_kernel_read() UML needs it's own probe_kernel_read() to handle kernel mode faults correctly. The implementation uses mincore() on the host side to detect whether a page is owned by the UML kernel process. This fixes also a possible crash when sysrq-t is used. Starting with 3.10 sysrq-t calls probe_kernel_read() to read details from the kernel workers. As kernel worker are completely async pointers may turn NULL while reading them. Cc: Cc: Cc: # 3.10.x Signed-off-by: Richard Weinberger --- arch/um/include/shared/os.h | 1 + arch/um/kernel/Makefile | 2 +- arch/um/kernel/maccess.c | 24 +++++++++++++++++++++ arch/um/os-Linux/process.c | 52 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 arch/um/kernel/maccess.c (limited to 'arch') diff --git a/arch/um/include/shared/os.h b/arch/um/include/shared/os.h index 95feaa47a2fb..c70a234a3f8c 100644 --- a/arch/um/include/shared/os.h +++ b/arch/um/include/shared/os.h @@ -200,6 +200,7 @@ extern int os_unmap_memory(void *addr, int len); extern int os_drop_memory(void *addr, int length); extern int can_drop_memory(void); extern void os_flush_stdout(void); +extern int os_mincore(void *addr, unsigned long len); /* execvp.c */ extern int execvp_noalloc(char *buf, const char *file, char *const argv[]); diff --git a/arch/um/kernel/Makefile b/arch/um/kernel/Makefile index babe21826e3e..d8b78a03855c 100644 --- a/arch/um/kernel/Makefile +++ b/arch/um/kernel/Makefile @@ -13,7 +13,7 @@ clean-files := obj-y = config.o exec.o exitcode.o irq.o ksyms.o mem.o \ physmem.o process.o ptrace.o reboot.o sigio.o \ signal.o smp.o syscall.o sysrq.o time.o tlb.o trap.o \ - um_arch.o umid.o skas/ + um_arch.o umid.o maccess.o skas/ obj-$(CONFIG_BLK_DEV_INITRD) += initrd.o obj-$(CONFIG_GPROF) += gprof_syms.o diff --git a/arch/um/kernel/maccess.c b/arch/um/kernel/maccess.c new file mode 100644 index 000000000000..1f3d5c4910d1 --- /dev/null +++ b/arch/um/kernel/maccess.c @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2013 Richard Weinberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +long probe_kernel_read(void *dst, const void *src, size_t size) +{ + void *psrc = (void *)rounddown((unsigned long)src, PAGE_SIZE); + + if ((unsigned long)src < PAGE_SIZE || size <= 0) + return -EFAULT; + + if (os_mincore(psrc, size + src - psrc) <= 0) + return -EFAULT; + + return __probe_kernel_read(dst, src, size); +} diff --git a/arch/um/os-Linux/process.c b/arch/um/os-Linux/process.c index b8f34c9e53ae..67b9c8f5a89e 100644 --- a/arch/um/os-Linux/process.c +++ b/arch/um/os-Linux/process.c @@ -4,6 +4,7 @@ */ #include +#include #include #include #include @@ -232,6 +233,57 @@ out: return ok; } +static int os_page_mincore(void *addr) +{ + char vec[2]; + int ret; + + ret = mincore(addr, UM_KERN_PAGE_SIZE, vec); + if (ret < 0) { + if (errno == ENOMEM || errno == EINVAL) + return 0; + else + return -errno; + } + + return vec[0] & 1; +} + +int os_mincore(void *addr, unsigned long len) +{ + char *vec; + int ret, i; + + if (len <= UM_KERN_PAGE_SIZE) + return os_page_mincore(addr); + + vec = calloc(1, (len + UM_KERN_PAGE_SIZE - 1) / UM_KERN_PAGE_SIZE); + if (!vec) + return -ENOMEM; + + ret = mincore(addr, UM_KERN_PAGE_SIZE, vec); + if (ret < 0) { + if (errno == ENOMEM || errno == EINVAL) + ret = 0; + else + ret = -errno; + + goto out; + } + + for (i = 0; i < ((len + UM_KERN_PAGE_SIZE - 1) / UM_KERN_PAGE_SIZE); i++) { + if (!(vec[i] & 1)) { + ret = 0; + goto out; + } + } + + ret = 1; +out: + free(vec); + return ret; +} + void init_new_thread_signals(void) { set_handler(SIGSEGV); -- cgit v1.2.3 From 805f11a0d515658106bfbfadceff0eb30bd90ad2 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Sun, 18 Aug 2013 13:30:06 +0200 Subject: um: ubd: Add REQ_FLUSH suppport UML's block device driver does not support write barriers, to support this this patch adds REQ_FLUSH suppport. Every time the block layer sends a REQ_FLUSH we fsync() now our backing file to guarantee data consistency. Reported-and-tested-by: Richard W.M. Jones Signed-off-by: Richard Weinberger --- arch/um/drivers/ubd_kern.c | 41 ++++++++++++++++++++++++++++++++++++++++- arch/um/include/shared/os.h | 1 + arch/um/os-Linux/file.c | 9 +++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/um/drivers/ubd_kern.c b/arch/um/drivers/ubd_kern.c index 879990cb66c6..d27c703be1a1 100644 --- a/arch/um/drivers/ubd_kern.c +++ b/arch/um/drivers/ubd_kern.c @@ -41,7 +41,7 @@ #include #include "cow.h" -enum ubd_req { UBD_READ, UBD_WRITE }; +enum ubd_req { UBD_READ, UBD_WRITE, UBD_FLUSH }; struct io_thread_req { struct request *req; @@ -866,6 +866,7 @@ static int ubd_add(int n, char **error_out) goto out; } ubd_dev->queue->queuedata = ubd_dev; + blk_queue_flush(ubd_dev->queue, REQ_FLUSH); blk_queue_max_segments(ubd_dev->queue, MAX_SG); err = ubd_disk_register(UBD_MAJOR, ubd_dev->size, n, &ubd_gendisk[n]); @@ -1238,6 +1239,19 @@ static void prepare_request(struct request *req, struct io_thread_req *io_req, } +/* Called with dev->lock held */ +static void prepare_flush_request(struct request *req, + struct io_thread_req *io_req) +{ + struct gendisk *disk = req->rq_disk; + struct ubd *ubd_dev = disk->private_data; + + io_req->req = req; + io_req->fds[0] = (ubd_dev->cow.file != NULL) ? ubd_dev->cow.fd : + ubd_dev->fd; + io_req->op = UBD_FLUSH; +} + /* Called with dev->lock held */ static void do_ubd_request(struct request_queue *q) { @@ -1259,6 +1273,20 @@ static void do_ubd_request(struct request_queue *q) } req = dev->request; + + if (req->cmd_flags & REQ_FLUSH) { + io_req = kmalloc(sizeof(struct io_thread_req), + GFP_ATOMIC); + if (io_req == NULL) { + if (list_empty(&dev->restart)) + list_add(&dev->restart, &restart); + return; + } + prepare_flush_request(req, io_req); + os_write_file(thread_fd, &io_req, + sizeof(struct io_thread_req *)); + } + while(dev->start_sg < dev->end_sg){ struct scatterlist *sg = &dev->sg[dev->start_sg]; @@ -1367,6 +1395,17 @@ static void do_io(struct io_thread_req *req) int err; __u64 off; + if (req->op == UBD_FLUSH) { + /* fds[0] is always either the rw image or our cow file */ + n = os_sync_file(req->fds[0]); + if (n != 0) { + printk("do_io - sync failed err = %d " + "fd = %d\n", -n, req->fds[0]); + req->error = 1; + } + return; + } + nsectors = req->length / req->sectorsize; start = 0; do { diff --git a/arch/um/include/shared/os.h b/arch/um/include/shared/os.h index c70a234a3f8c..e98303925cc5 100644 --- a/arch/um/include/shared/os.h +++ b/arch/um/include/shared/os.h @@ -141,6 +141,7 @@ extern int os_seek_file(int fd, unsigned long long offset); extern int os_open_file(const char *file, struct openflags flags, int mode); extern int os_read_file(int fd, void *buf, int len); extern int os_write_file(int fd, const void *buf, int count); +extern int os_sync_file(int fd); extern int os_file_size(const char *file, unsigned long long *size_out); extern int os_file_modtime(const char *file, unsigned long *modtime); extern int os_pipe(int *fd, int stream, int close_on_exec); diff --git a/arch/um/os-Linux/file.c b/arch/um/os-Linux/file.c index c17bd6f7d674..07a750197bb0 100644 --- a/arch/um/os-Linux/file.c +++ b/arch/um/os-Linux/file.c @@ -266,6 +266,15 @@ int os_write_file(int fd, const void *buf, int len) return n; } +int os_sync_file(int fd) +{ + int n = fsync(fd); + + if (n < 0) + return -errno; + return n; +} + int os_file_size(const char *file, unsigned long long *size_out) { struct uml_stat buf; -- cgit v1.2.3 From bc1d72e73be63a7c4a07eb10cf51e91f20bf6076 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Sun, 18 Aug 2013 13:30:07 +0200 Subject: um: ubd: Introduce submit_request() Just a clean-up patch to remove the open coded variants and to ensure that all requests are submitted the same way. Signed-off-by: Richard Weinberger --- arch/um/drivers/ubd_kern.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) (limited to 'arch') diff --git a/arch/um/drivers/ubd_kern.c b/arch/um/drivers/ubd_kern.c index d27c703be1a1..1812bc81715b 100644 --- a/arch/um/drivers/ubd_kern.c +++ b/arch/um/drivers/ubd_kern.c @@ -1252,12 +1252,28 @@ static void prepare_flush_request(struct request *req, io_req->op = UBD_FLUSH; } +static bool submit_request(struct io_thread_req *io_req, struct ubd *dev) +{ + int n = os_write_file(thread_fd, &io_req, + sizeof(io_req)); + if (n != sizeof(io_req)) { + if (n != -EAGAIN) + printk("write to io thread failed, " + "errno = %d\n", -n); + else if (list_empty(&dev->restart)) + list_add(&dev->restart, &restart); + + kfree(io_req); + return false; + } + return true; +} + /* Called with dev->lock held */ static void do_ubd_request(struct request_queue *q) { struct io_thread_req *io_req; struct request *req; - int n; while(1){ struct ubd *dev = q->queuedata; @@ -1283,8 +1299,7 @@ static void do_ubd_request(struct request_queue *q) return; } prepare_flush_request(req, io_req); - os_write_file(thread_fd, &io_req, - sizeof(struct io_thread_req *)); + submit_request(io_req, dev); } while(dev->start_sg < dev->end_sg){ @@ -1301,17 +1316,8 @@ static void do_ubd_request(struct request_queue *q) (unsigned long long)dev->rq_pos << 9, sg->offset, sg->length, sg_page(sg)); - n = os_write_file(thread_fd, &io_req, - sizeof(struct io_thread_req *)); - if(n != sizeof(struct io_thread_req *)){ - if(n != -EAGAIN) - printk("write to io thread failed, " - "errno = %d\n", -n); - else if(list_empty(&dev->restart)) - list_add(&dev->restart, &restart); - kfree(io_req); + if (submit_request(io_req, dev) == false) return; - } dev->rq_pos += sg->length >> 9; dev->start_sg++; -- cgit v1.2.3 From 91d44ff860a9e9c0db81a89cbc24fa31fbd8e6d3 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Sun, 18 Aug 2013 13:30:08 +0200 Subject: um: Cleanup SIGTERM handling Richard reported that some UML processes survive if the UML main process receives a SIGTERM. This issue was caused by a wrongly placed signal(SIGTERM, SIG_DFL) in init_new_thread_signals(). It disabled the UML exit handler accidently for some processes. The correct solution is to disable the fatal handler for all UML helper threads/processes. Such that last_ditch_exit() does not get called multiple times and all processes can exit due to SIGTERM. Reported-and-tested-by: Richard W.M. Jones Signed-off-by: Richard Weinberger --- arch/um/drivers/ubd.h | 1 - arch/um/drivers/ubd_kern.c | 3 ++- arch/um/drivers/ubd_user.c | 5 ----- arch/um/include/shared/os.h | 1 + arch/um/os-Linux/aio.c | 5 ++--- arch/um/os-Linux/process.c | 1 - arch/um/os-Linux/sigio.c | 2 +- arch/um/os-Linux/util.c | 10 ++++++++++ 8 files changed, 16 insertions(+), 12 deletions(-) (limited to 'arch') diff --git a/arch/um/drivers/ubd.h b/arch/um/drivers/ubd.h index 3845051f1b10..3b48cd2081ee 100644 --- a/arch/um/drivers/ubd.h +++ b/arch/um/drivers/ubd.h @@ -7,7 +7,6 @@ #ifndef __UM_UBD_USER_H #define __UM_UBD_USER_H -extern void ignore_sigwinch_sig(void); extern int start_io_thread(unsigned long sp, int *fds_out); extern int io_thread(void *arg); extern int kernel_fd; diff --git a/arch/um/drivers/ubd_kern.c b/arch/um/drivers/ubd_kern.c index 1812bc81715b..3716e6952554 100644 --- a/arch/um/drivers/ubd_kern.c +++ b/arch/um/drivers/ubd_kern.c @@ -1476,7 +1476,8 @@ int io_thread(void *arg) struct io_thread_req *req; int n; - ignore_sigwinch_sig(); + os_fix_helper_signals(); + while(1){ n = os_read_file(kernel_fd, &req, sizeof(struct io_thread_req *)); diff --git a/arch/um/drivers/ubd_user.c b/arch/um/drivers/ubd_user.c index a703e45d8aac..e376f9b9c68d 100644 --- a/arch/um/drivers/ubd_user.c +++ b/arch/um/drivers/ubd_user.c @@ -21,11 +21,6 @@ #include "ubd.h" #include -void ignore_sigwinch_sig(void) -{ - signal(SIGWINCH, SIG_IGN); -} - int start_io_thread(unsigned long sp, int *fd_out) { int pid, fds[2], err; diff --git a/arch/um/include/shared/os.h b/arch/um/include/shared/os.h index e98303925cc5..021104d98cb3 100644 --- a/arch/um/include/shared/os.h +++ b/arch/um/include/shared/os.h @@ -235,6 +235,7 @@ extern void setup_machinename(char *machine_out); extern void setup_hostinfo(char *buf, int len); extern void os_dump_core(void) __attribute__ ((noreturn)); extern void um_early_printk(const char *s, unsigned int n); +extern void os_fix_helper_signals(void); /* time.c */ extern void idle_sleep(unsigned long long nsecs); diff --git a/arch/um/os-Linux/aio.c b/arch/um/os-Linux/aio.c index 3a6bc2af0961..014eb35fd13b 100644 --- a/arch/um/os-Linux/aio.c +++ b/arch/um/os-Linux/aio.c @@ -104,8 +104,7 @@ static int aio_thread(void *arg) struct io_event event; int err, n, reply_fd; - signal(SIGWINCH, SIG_IGN); - + os_fix_helper_signals(); while (1) { n = io_getevents(ctx, 1, 1, &event, NULL); if (n < 0) { @@ -173,7 +172,7 @@ static int not_aio_thread(void *arg) struct aio_thread_reply reply; int err; - signal(SIGWINCH, SIG_IGN); + os_fix_helper_signals(); while (1) { err = read(aio_req_fd_r, &req, sizeof(req)); if (err != sizeof(req)) { diff --git a/arch/um/os-Linux/process.c b/arch/um/os-Linux/process.c index 67b9c8f5a89e..33496fe2bb52 100644 --- a/arch/um/os-Linux/process.c +++ b/arch/um/os-Linux/process.c @@ -294,5 +294,4 @@ void init_new_thread_signals(void) signal(SIGHUP, SIG_IGN); set_handler(SIGIO); signal(SIGWINCH, SIG_IGN); - signal(SIGTERM, SIG_DFL); } diff --git a/arch/um/os-Linux/sigio.c b/arch/um/os-Linux/sigio.c index 8b61cc0e82c8..46e762f926eb 100644 --- a/arch/um/os-Linux/sigio.c +++ b/arch/um/os-Linux/sigio.c @@ -55,7 +55,7 @@ static int write_sigio_thread(void *unused) int i, n, respond_fd; char c; - signal(SIGWINCH, SIG_IGN); + os_fix_helper_signals(); fds = ¤t_poll; while (1) { n = poll(fds->poll, fds->used, -1); diff --git a/arch/um/os-Linux/util.c b/arch/um/os-Linux/util.c index 492ef5e6e166..faee55ef6d2f 100644 --- a/arch/um/os-Linux/util.c +++ b/arch/um/os-Linux/util.c @@ -94,6 +94,16 @@ static inline void __attribute__ ((noreturn)) uml_abort(void) exit(127); } +/* + * UML helper threads must not handle SIGWINCH/INT/TERM + */ +void os_fix_helper_signals(void) +{ + signal(SIGWINCH, SIG_IGN); + signal(SIGINT, SIG_DFL); + signal(SIGTERM, SIG_DFL); +} + void os_dump_core(void) { int pid; -- cgit v1.2.3 From 250127216dd6635f5730752d7444bdec992aecb0 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Sun, 18 Aug 2013 13:30:09 +0200 Subject: um: Run UML in it's own session. If UML is not run by a shell it can happen that UML will kill unrelated proceses upon a fatal exit because it issues a kill(0, ...). To prevent such oddities we create a new session in main(). Reported-and-tested-by: Richard W.M. Jones Signed-off-by: Richard Weinberger --- arch/um/os-Linux/main.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'arch') diff --git a/arch/um/os-Linux/main.c b/arch/um/os-Linux/main.c index 749c96da7b99..e1704ff600ff 100644 --- a/arch/um/os-Linux/main.c +++ b/arch/um/os-Linux/main.c @@ -123,6 +123,8 @@ int __init main(int argc, char **argv, char **envp) setup_env_path(); + setsid(); + new_argv = malloc((argc + 1) * sizeof(char *)); if (new_argv == NULL) { perror("Mallocing argv"); -- cgit v1.2.3 From b5c04da022e801ca04057e09de15f876fd203ef1 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Fri, 23 Aug 2013 13:37:49 +0200 Subject: um: prctl: Do not include linux/ptrace.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On recent toolchains we hit: In file included from arch/x86/um/os-Linux/prctl.c:7:0: /usr/include/linux/ptrace.h:58:8: error: redefinition of ‘struct ptrace_peeksiginfo_args’ struct ptrace_peeksiginfo_args { ^ In file included from arch/x86/um/os-Linux/prctl.c:6:0: /usr/include/sys/ptrace.h:191:8: note: originally defined here struct ptrace_peeksiginfo_args ^ make[2]: *** [arch/x86/um/os-Linux/prctl.o] Error 1 make[1]: *** [arch/x86/um/os-Linux] Error 2 make: *** [arch/x86/um] Error 2 The solution is not to include linux/ptrace.h and obtain the arch specific ptrace command from asm/ptrace.h. Reported-and-tested-by: David Oberhollenzer Signed-off-by: Richard Weinberger --- arch/x86/um/os-Linux/prctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/x86/um/os-Linux/prctl.c b/arch/x86/um/os-Linux/prctl.c index 9d34eddb517f..96eb2bd28832 100644 --- a/arch/x86/um/os-Linux/prctl.c +++ b/arch/x86/um/os-Linux/prctl.c @@ -4,7 +4,7 @@ */ #include -#include +#include int os_arch_prctl(int pid, int code, unsigned long *addr) { -- cgit v1.2.3 From 81bab4c38ad59b35d1bea2a96ce6bc90570b1392 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Mon, 2 Sep 2013 22:49:22 +0200 Subject: um: Add irq chip um/mask handlers These handlers are not optional and need in our case dummy implementions to avoid NULL pointer bugs within the irq core code. Reported-and-tested-by: Toralf Foester Signed-off-by: Richard Weinberger --- arch/um/kernel/irq.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'arch') diff --git a/arch/um/kernel/irq.c b/arch/um/kernel/irq.c index 36e12f0cefd5..1d8505b1e290 100644 --- a/arch/um/kernel/irq.c +++ b/arch/um/kernel/irq.c @@ -337,6 +337,8 @@ static struct irq_chip normal_irq_type = { .irq_disable = dummy, .irq_enable = dummy, .irq_ack = dummy, + .irq_mask = dummy, + .irq_unmask = dummy, }; static struct irq_chip SIGVTALRM_irq_type = { @@ -344,6 +346,8 @@ static struct irq_chip SIGVTALRM_irq_type = { .irq_disable = dummy, .irq_enable = dummy, .irq_ack = dummy, + .irq_mask = dummy, + .irq_unmask = dummy, }; void __init init_IRQ(void) -- cgit v1.2.3