Commit 345b17ac authored by Linus Torvalds's avatar Linus Torvalds

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

Pull UML updates from Richard Weinberger:

 - IRQ handling cleanups

 - Support for suspend

 - Various fixes for UML specific drivers: ubd, vector, xterm

* tag 'for-linus-5.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rw/uml: (32 commits)
  um: Fix build w/o CONFIG_PM_SLEEP
  um: time-travel: Correct time event IRQ delivery
  um: irq/sigio: Support suspend/resume handling of workaround IRQs
  um: time-travel: Actually apply "free-until" optimisation
  um: chan_xterm: Fix fd leak
  um: tty: Fix handling of close in tty lines
  um: Monitor error events in IRQ controller
  um: allocate a guard page to helper threads
  um: support some of ARCH_HAS_SET_MEMORY
  um: time-travel: avoid multiple identical propagations
  um: Fetch registers only for signals which need them
  um: Support suspend to RAM
  um: Allow PM with suspend-to-idle
  um: time: Fix read_persistent_clock64() in time-travel
  um: Simplify os_idle_sleep() and sleep longer
  um: Simplify IRQ handling code
  um: Remove IRQ_NONE type
  um: irq: Reduce irq_reg allocation
  um: irq: Clean up and rename struct irq_fd
  um: Clean up alarm IRQ chip name
  ...
parents 787fec8a 1fb1abc8
......@@ -15,6 +15,7 @@ config UML
select HAVE_DEBUG_KMEMLEAK
select HAVE_DEBUG_BUGVERBOSE
select NO_DMA
select ARCH_HAS_SET_MEMORY
select GENERIC_IRQ_SHOW
select GENERIC_CPU_DEVICES
select HAVE_GCC_PLUGINS
......@@ -191,3 +192,8 @@ config UML_TIME_TRAVEL_SUPPORT
endmenu
source "arch/um/drivers/Kconfig"
config ARCH_SUSPEND_POSSIBLE
def_bool y
source "kernel/power/Kconfig"
......@@ -26,10 +26,10 @@ int generic_read(int fd, char *c_out, void *unused)
n = read(fd, c_out, sizeof(*c_out));
if (n > 0)
return n;
else if (errno == EAGAIN)
return 0;
else if (n == 0)
return -EIO;
else if (errno == EAGAIN)
return 0;
return -errno;
}
......
......@@ -262,19 +262,25 @@ 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;
int err;
if (input)
if (input) {
err = um_request_irq(driver->read_irq, fd, IRQ_READ,
line_interrupt, IRQF_SHARED,
driver->read_irq_name, data);
if (err)
return err;
if (output)
if (err < 0)
return err;
}
if (output) {
err = um_request_irq(driver->write_irq, fd, IRQ_WRITE,
line_write_interrupt, IRQF_SHARED,
driver->write_irq_name, data);
return err;
if (err < 0)
return err;
}
return 0;
}
static int line_activate(struct tty_port *port, struct tty_struct *tty)
......@@ -608,7 +614,6 @@ static void free_winch(struct winch *winch)
winch->fd = -1;
if (fd != -1)
os_close_file(fd);
list_del(&winch->list);
__free_winch(&winch->work);
}
......@@ -709,6 +714,8 @@ static void unregister_winch(struct tty_struct *tty)
winch = list_entry(ele, struct winch, list);
wtty = tty_port_tty_get(winch->port);
if (wtty == tty) {
list_del(&winch->list);
spin_unlock(&winch_handler_lock);
free_winch(winch);
break;
}
......@@ -719,14 +726,17 @@ static void unregister_winch(struct tty_struct *tty)
static void winch_cleanup(void)
{
struct list_head *ele, *next;
struct winch *winch;
spin_lock(&winch_handler_lock);
while ((winch = list_first_entry_or_null(&winch_handlers,
struct winch, list))) {
list_del(&winch->list);
spin_unlock(&winch_handler_lock);
list_for_each_safe(ele, next, &winch_handlers) {
winch = list_entry(ele, struct winch, list);
free_winch(winch);
spin_lock(&winch_handler_lock);
}
spin_unlock(&winch_handler_lock);
......
......@@ -738,7 +738,7 @@ static int __init mconsole_init(void)
err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
IRQF_SHARED, "mconsole", (void *)sock);
if (err) {
if (err < 0) {
printk(KERN_ERR "Failed to get IRQ for management console\n");
goto out;
}
......
......@@ -160,7 +160,7 @@ static int uml_net_open(struct net_device *dev)
err = um_request_irq(dev->irq, lp->fd, IRQ_READ, uml_net_interrupt,
IRQF_SHARED, dev->name, dev);
if (err != 0) {
if (err < 0) {
printk(KERN_ERR "uml_net_open: failed to get irq(%d)\n", err);
err = -ENETUNREACH;
goto out_close;
......
......@@ -100,7 +100,7 @@ static int port_accept(struct port_list *port)
.port = port });
if (um_request_irq(TELNETD_IRQ, socket[0], IRQ_READ, pipe_interrupt,
IRQF_SHARED, "telnetd", conn)) {
IRQF_SHARED, "telnetd", conn) < 0) {
printk(KERN_ERR "port_accept : failed to get IRQ for "
"telnetd\n");
goto out_free;
......@@ -182,7 +182,7 @@ void *port_data(int port_num)
}
if (um_request_irq(ACCEPT_IRQ, fd, IRQ_READ, port_interrupt,
IRQF_SHARED, "port", port)) {
IRQF_SHARED, "port", port) < 0) {
printk(KERN_ERR "Failed to get IRQ for port %d\n", port_num);
goto out_close;
}
......
......@@ -11,6 +11,7 @@
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/miscdevice.h>
#include <linux/hw_random.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <init.h>
......@@ -18,9 +19,8 @@
#include <os.h>
/*
* core module and version information
* core module information
*/
#define RNG_VERSION "1.0.0"
#define RNG_MODULE_NAME "hw_random"
/* Changed at init time, in the non-modular case, and at module load
......@@ -28,88 +28,36 @@
* protects against a module being loaded twice at the same time.
*/
static int random_fd = -1;
static DECLARE_WAIT_QUEUE_HEAD(host_read_wait);
static struct hwrng hwrng = { 0, };
static DECLARE_COMPLETION(have_data);
static int rng_dev_open (struct inode *inode, struct file *filp)
static int rng_dev_read(struct hwrng *rng, void *buf, size_t max, bool block)
{
/* enforce read-only access to this chrdev */
if ((filp->f_mode & FMODE_READ) == 0)
return -EINVAL;
if ((filp->f_mode & FMODE_WRITE) != 0)
return -EINVAL;
int ret;
return 0;
}
static atomic_t host_sleep_count = ATOMIC_INIT(0);
static ssize_t rng_dev_read (struct file *filp, char __user *buf, size_t size,
loff_t *offp)
{
u32 data;
int n, ret = 0, have_data;
while (size) {
n = os_read_file(random_fd, &data, sizeof(data));
if (n > 0) {
have_data = n;
while (have_data && size) {
if (put_user((u8) data, buf++)) {
ret = ret ? : -EFAULT;
break;
}
size--;
ret++;
have_data--;
data >>= 8;
}
}
else if (n == -EAGAIN) {
DECLARE_WAITQUEUE(wait, current);
if (filp->f_flags & O_NONBLOCK)
return ret ? : -EAGAIN;
atomic_inc(&host_sleep_count);
for (;;) {
ret = os_read_file(random_fd, buf, max);
if (block && ret == -EAGAIN) {
add_sigio_fd(random_fd);
add_wait_queue(&host_read_wait, &wait);
set_current_state(TASK_INTERRUPTIBLE);
ret = wait_for_completion_killable(&have_data);
schedule();
remove_wait_queue(&host_read_wait, &wait);
ignore_sigio_fd(random_fd);
deactivate_fd(random_fd, RANDOM_IRQ);
if (atomic_dec_and_test(&host_sleep_count)) {
ignore_sigio_fd(random_fd);
deactivate_fd(random_fd, RANDOM_IRQ);
}
if (ret < 0)
break;
} else {
break;
}
else
return n;
if (signal_pending (current))
return ret ? : -ERESTARTSYS;
}
return ret;
}
static const struct file_operations rng_chrdev_ops = {
.owner = THIS_MODULE,
.open = rng_dev_open,
.read = rng_dev_read,
.llseek = noop_llseek,
};
/* rng_init shouldn't be called more than once at boot time */
static struct miscdevice rng_miscdev = {
HWRNG_MINOR,
RNG_MODULE_NAME,
&rng_chrdev_ops,
};
return ret != -EAGAIN ? ret : 0;
}
static irqreturn_t random_interrupt(int irq, void *data)
{
wake_up(&host_read_wait);
complete(&have_data);
return IRQ_HANDLED;
}
......@@ -126,18 +74,19 @@ static int __init rng_init (void)
goto out;
random_fd = err;
err = um_request_irq(RANDOM_IRQ, random_fd, IRQ_READ, random_interrupt,
0, "random", NULL);
if (err)
if (err < 0)
goto err_out_cleanup_hw;
sigio_broken(random_fd, 1);
sigio_broken(random_fd);
hwrng.name = RNG_MODULE_NAME;
hwrng.read = rng_dev_read;
hwrng.quality = 1024;
err = misc_register (&rng_miscdev);
err = hwrng_register(&hwrng);
if (err) {
printk (KERN_ERR RNG_MODULE_NAME ": misc device register "
"failed\n");
pr_err(RNG_MODULE_NAME " registering failed (%d)\n", err);
goto err_out_cleanup_hw;
}
out:
......@@ -161,8 +110,8 @@ static void cleanup(void)
static void __exit rng_cleanup(void)
{
hwrng_unregister(&hwrng);
os_close_file(random_fd);
misc_deregister (&rng_miscdev);
}
module_init (rng_init);
......
This diff is collapsed.
......@@ -1196,9 +1196,9 @@ static int vector_net_close(struct net_device *dev)
/* TX tasklet */
static void vector_tx_poll(unsigned long data)
static void vector_tx_poll(struct tasklet_struct *t)
{
struct vector_private *vp = (struct vector_private *)data;
struct vector_private *vp = from_tasklet(vp, t, tx_poll);
vp->estats.tx_kicks++;
vector_send(vp->tx_queue);
......@@ -1271,7 +1271,7 @@ static int vector_net_open(struct net_device *dev)
irq_rr + VECTOR_BASE_IRQ, vp->fds->rx_fd,
IRQ_READ, vector_rx_interrupt,
IRQF_SHARED, dev->name, dev);
if (err != 0) {
if (err < 0) {
netdev_err(dev, "vector_open: failed to get rx irq(%d)\n", err);
err = -ENETUNREACH;
goto out_close;
......@@ -1286,7 +1286,7 @@ static int vector_net_open(struct net_device *dev)
irq_rr + VECTOR_BASE_IRQ, vp->fds->tx_fd,
IRQ_WRITE, vector_tx_interrupt,
IRQF_SHARED, dev->name, dev);
if (err != 0) {
if (err < 0) {
netdev_err(dev,
"vector_open: failed to get tx irq(%d)\n", err);
err = -ENETUNREACH;
......@@ -1629,7 +1629,7 @@ static void vector_eth_configure(
});
dev->features = dev->hw_features = (NETIF_F_SG | NETIF_F_FRAGLIST);
tasklet_init(&vp->tx_poll, vector_tx_poll, (unsigned long)vp);
tasklet_setup(&vp->tx_poll, vector_tx_poll);
INIT_WORK(&vp->reset_tx, vector_reset_tx);
timer_setup(&vp->tl, vector_timer_expire, 0);
......
......@@ -33,11 +33,6 @@
#include <os.h>
#include "vhost_user.h"
/* Workaround due to a conflict between irq_user.h and irqreturn.h */
#ifdef IRQ_NONE
#undef IRQ_NONE
#endif
#define MAX_SUPPORTED_QUEUE_SIZE 256
#define to_virtio_uml_device(_vdev) \
......@@ -55,7 +50,7 @@ struct virtio_uml_device {
struct platform_device *pdev;
spinlock_t sock_lock;
int sock, req_fd;
int sock, req_fd, irq;
u64 features;
u64 protocol_features;
u8 status;
......@@ -409,12 +404,14 @@ static int vhost_user_init_slave_req(struct virtio_uml_device *vu_dev)
return rc;
vu_dev->req_fd = req_fds[0];
rc = um_request_irq(VIRTIO_IRQ, vu_dev->req_fd, IRQ_READ,
rc = um_request_irq(UM_IRQ_ALLOC, vu_dev->req_fd, IRQ_READ,
vu_req_interrupt, IRQF_SHARED,
vu_dev->pdev->name, vu_dev);
if (rc)
if (rc < 0)
goto err_close;
vu_dev->irq = rc;
rc = vhost_user_send_no_payload_fd(vu_dev, VHOST_USER_SET_SLAVE_REQ_FD,
req_fds[1]);
if (rc)
......@@ -423,7 +420,7 @@ static int vhost_user_init_slave_req(struct virtio_uml_device *vu_dev)
goto out;
err_free_irq:
um_free_irq(VIRTIO_IRQ, vu_dev);
um_free_irq(vu_dev->irq, vu_dev);
err_close:
os_close_file(req_fds[0]);
out:
......@@ -802,7 +799,11 @@ static void vu_del_vq(struct virtqueue *vq)
struct virtio_uml_vq_info *info = vq->priv;
if (info->call_fd >= 0) {
um_free_irq(VIRTIO_IRQ, vq);
struct virtio_uml_device *vu_dev;
vu_dev = to_virtio_uml_device(vq->vdev);
um_free_irq(vu_dev->irq, vq);
os_close_file(info->call_fd);
}
......@@ -852,9 +853,9 @@ static int vu_setup_vq_call_fd(struct virtio_uml_device *vu_dev,
return rc;
info->call_fd = call_fds[0];
rc = um_request_irq(VIRTIO_IRQ, info->call_fd, IRQ_READ,
rc = um_request_irq(vu_dev->irq, info->call_fd, IRQ_READ,
vu_interrupt, IRQF_SHARED, info->name, vq);
if (rc)
if (rc < 0)
goto close_both;
rc = vhost_user_set_vring_call(vu_dev, vq->index, call_fds[1]);
......@@ -864,7 +865,7 @@ static int vu_setup_vq_call_fd(struct virtio_uml_device *vu_dev,
goto out;
release_irq:
um_free_irq(VIRTIO_IRQ, vq);
um_free_irq(vu_dev->irq, vq);
close_both:
os_close_file(call_fds[0]);
out:
......@@ -969,7 +970,7 @@ static struct virtqueue *vu_setup_vq(struct virtio_device *vdev,
error_setup:
if (info->call_fd >= 0) {
um_free_irq(VIRTIO_IRQ, vq);
um_free_irq(vu_dev->irq, vq);
os_close_file(info->call_fd);
}
error_call:
......@@ -1078,7 +1079,7 @@ static void virtio_uml_release_dev(struct device *d)
/* might not have been opened due to not negotiating the feature */
if (vu_dev->req_fd >= 0) {
um_free_irq(VIRTIO_IRQ, vu_dev);
um_free_irq(vu_dev->irq, vu_dev);
os_close_file(vu_dev->req_fd);
}
......
......@@ -18,6 +18,7 @@
struct xterm_chan {
int pid;
int helper_pid;
int chan_fd;
char *title;
int device;
int raw;
......@@ -33,6 +34,7 @@ static void *xterm_init(char *str, int device, const struct chan_opts *opts)
return NULL;
*data = ((struct xterm_chan) { .pid = -1,
.helper_pid = -1,
.chan_fd = -1,
.device = device,
.title = opts->xterm_title,
.raw = opts->raw } );
......@@ -149,6 +151,7 @@ static int xterm_open(int input, int output, int primary, void *d,
goto out_kill;
}
data->chan_fd = fd;
new = xterm_fd(fd, &data->helper_pid);
if (new < 0) {
err = new;
......@@ -206,6 +209,8 @@ static void xterm_close(int fd, void *d)
os_kill_process(data->helper_pid, 0);
data->helper_pid = -1;
if (data->chan_fd != -1)
os_close_file(data->chan_fd);
os_close_file(fd);
}
......
......@@ -51,7 +51,7 @@ int xterm_fd(int socket, int *pid_out)
err = um_request_irq(XTERM_IRQ, socket, IRQ_READ, xterm_interrupt,
IRQF_SHARED, "xterm", data);
if (err) {
if (err < 0) {
printk(KERN_ERR "xterm_fd : failed to get IRQ for xterm, "
"err = %d\n", err);
ret = err;
......
......@@ -17,21 +17,20 @@
#define TELNETD_IRQ 12
#define XTERM_IRQ 13
#define RANDOM_IRQ 14
#define VIRTIO_IRQ 15
#ifdef CONFIG_UML_NET_VECTOR
#define VECTOR_BASE_IRQ (VIRTIO_IRQ + 1)
#define VECTOR_BASE_IRQ (RANDOM_IRQ + 1)
#define VECTOR_IRQ_SPACE 8
#define LAST_IRQ (VECTOR_IRQ_SPACE + VECTOR_BASE_IRQ - 1)
#define UM_FIRST_DYN_IRQ (VECTOR_IRQ_SPACE + VECTOR_BASE_IRQ)
#else
#define LAST_IRQ VIRTIO_IRQ
#define UM_FIRST_DYN_IRQ (RANDOM_IRQ + 1)
#endif
#define NR_IRQS (LAST_IRQ + 1)
#define NR_IRQS 64
#endif
......@@ -55,12 +55,15 @@ extern unsigned long end_iomem;
#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY)
#define __PAGE_KERNEL_EXEC \
(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED)
#define __PAGE_KERNEL_RO \
(_PAGE_PRESENT | _PAGE_DIRTY | _PAGE_ACCESSED)
#define PAGE_NONE __pgprot(_PAGE_PROTNONE | _PAGE_ACCESSED)
#define PAGE_SHARED __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED)
#define PAGE_COPY __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED)
#define PAGE_READONLY __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED)
#define PAGE_KERNEL __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED)
#define PAGE_KERNEL_EXEC __pgprot(__PAGE_KERNEL_EXEC)
#define PAGE_KERNEL_RO __pgprot(__PAGE_KERNEL_RO)
/*
* The i386 can't do page protection for execute, and considers that the same
......
#include <asm-generic/set_memory.h>
......@@ -28,7 +28,7 @@ struct time_travel_event {
extern enum time_travel_mode time_travel_mode;
void time_travel_sleep(unsigned long long duration);
void time_travel_sleep(void);
static inline void
time_travel_set_event_fn(struct time_travel_event *e,
......@@ -60,7 +60,7 @@ struct time_travel_event {
#define time_travel_mode TT_MODE_OFF
static inline void time_travel_sleep(unsigned long long duration)
static inline void time_travel_sleep(void)
{
}
......
......@@ -40,3 +40,6 @@ DEFINE(UML_CONFIG_UML_X86, CONFIG_UML_X86);
#ifdef CONFIG_64BIT
DEFINE(UML_CONFIG_64BIT, CONFIG_64BIT);
#endif
#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
DEFINE(UML_CONFIG_UML_TIME_TRAVEL_SUPPORT, CONFIG_UML_TIME_TRAVEL_SUPPORT);
#endif
......@@ -8,11 +8,12 @@
#include <linux/interrupt.h>
#include <asm/ptrace.h>
#include "irq_user.h"
extern int um_request_irq(unsigned int irq, int fd, int type,
irq_handler_t handler,
unsigned long irqflags, const char * devname,
void *dev_id);
void um_free_irq(unsigned int irq, void *dev);
#endif
#define UM_IRQ_ALLOC -1
int um_request_irq(int irq, int fd, enum um_irq_type type,
irq_handler_t handler, unsigned long irqflags,
const char *devname, void *dev_id);
void um_free_irq(int irq, void *dev_id);
#endif
......@@ -9,25 +9,12 @@
#include <sysdep/ptrace.h>
#include <stdbool.h>
struct irq_fd {
struct irq_fd *next;
void *id;
int fd;
int type;
int irq;
int events;
bool active;
bool pending;
bool purge;
enum um_irq_type {
IRQ_READ,
IRQ_WRITE,
NUM_IRQ_TYPES,
};
#define IRQ_READ 0
#define IRQ_WRITE 1
#define IRQ_NONE 2
#define MAX_IRQ_TYPE (IRQ_NONE + 1)
struct siginfo;
extern void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs);
extern void free_irq_by_fd(int fd);
......
......@@ -19,7 +19,7 @@ extern int kmalloc_ok;
#define UML_ROUND_UP(addr) \
((((unsigned long) addr) + PAGE_SIZE - 1) & PAGE_MASK)
extern unsigned long alloc_stack(int order, int atomic);
extern unsigned long alloc_stack(int atomic);
extern void free_stack(unsigned long stack, int order);
struct pt_regs;
......@@ -39,6 +39,8 @@ extern int is_syscall(unsigned long addr);
extern void timer_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs);
extern void uml_pm_wake(void);
extern int start_uml(void);
extern void paging_init(void);
......@@ -66,5 +68,6 @@ extern void bus_handler(int sig, struct siginfo *si, struct uml_pt_regs *regs);
extern void winch(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs);
extern void fatal_sigsegv(void) __attribute__ ((noreturn));
void um_idle_sleep(void);
#endif
......@@ -233,6 +233,7 @@ extern void timer_set_signal_handler(void);
extern void set_sigstack(void *sig_stack, int size);
extern void remove_sigstack(void);
extern void set_handler(int sig);
extern void send_sigio_to_self(void);
extern int change_sig(int signal, int on);
extern void block_signals(void);
extern void unblock_signals(void);
......@@ -241,6 +242,7 @@ extern int set_signals(int enable);
extern int set_signals_trace(int enable);
extern int os_is_signal_stack(void);
extern void deliver_alarm(void);
extern void register_pm_wake_signal(void);
/* util.c */
extern void stack_protections(unsigned long address);
......@@ -256,7 +258,7 @@ extern void os_warn(const char *fmt, ...)
__attribute__ ((format (printf, 1, 2)));
/* time.c */
extern void os_idle_sleep(unsigned long long nsecs);
extern void os_idle_sleep(void);
extern int os_timer_create(void);
extern int os_timer_set_interval(unsigned long long nsecs);
extern int os_timer_one_shot(unsigned long long nsecs);
......@@ -299,19 +301,29 @@ extern void reboot_skas(void);
extern int os_waiting_for_events_epoll(void);
extern void *os_epoll_get_data_pointer(int index);
extern int os_epoll_triggered(int index, int events);
extern int os_event_mask(int irq_type);
extern int os_event_mask(enum um_irq_type irq_type);
extern int os_setup_epoll(void);
extern int os_add_epoll_fd(int events, int fd, void *data);
extern int os_mod_epoll_fd(int events, int fd, void *data);
extern int os_del_epoll_fd(int fd);
extern void os_set_ioignore(void);
extern void os_close_epoll_fd(void);
extern void um_irqs_suspend(void);
extern void um_irqs_resume(void);
/* sigio.c */
extern int add_sigio_fd(int fd);
extern int ignore_sigio_fd(int fd);
extern void maybe_sigio_broken(int fd, int read);
extern void sigio_broken(int fd, int read);
extern void maybe_sigio_broken(int fd);
extern void sigio_broken(int fd);
/*
* unlocked versions for IRQ controller code.
*
* This is safe because it's used at suspend/resume and nothing
* else is running.
*/
extern int __add_sigio_fd(int fd);
extern int __ignore_sigio_fd(int fd);
/* prctl.c */
extern int os_arch_prctl(int pid, int option, unsigned long *arg2);
......@@ -330,4 +342,7 @@ extern void unblock_signals_trace(void);
extern void um_trace_signals_on(void);
extern void um_trace_signals_off(void);
/* time-travel */
extern void deliver_time_travel_irqs(void);
#endif
This diff is collapsed.
......@@ -32,6 +32,7 @@
#include <os.h>
#include <skas.h>
#include <linux/time-internal.h>
#include <asm/set_memory.h>
/*
* This is a per-cpu array. A processor only modifies its entry and it only
......@@ -62,16 +63,18 @@ void free_stack(unsigned long stack, int order)
free_pages(stack, order);
}
unsigned long alloc_stack(int order, int atomic)
unsigned long alloc_stack(int atomic)
{
unsigned long page;
unsigned long addr;
gfp_t flags = GFP_KERNEL;
if (atomic)
flags = GFP_ATOMIC;
page = __get_free_pages(flags, order);
addr = __get_free_pages(flags, 1);
return page;
set_memory_ro(addr, 1);
return addr + PAGE_SIZE;
}
static inline void set_current(struct task_struct *task)
......@@ -203,15 +206,12 @@ void initial_thread_cb(void (*proc)(void *), void *arg)
kmalloc_ok = save_kmalloc_ok;
}
static void um_idle_sleep(void)
void um_idle_sleep(void)
{
unsigned long long duration = UM_NSEC_PER_SEC;
if (time_travel_mode != TT_MODE_OFF) {
time_travel_sleep(duration);
} else {
os_idle_sleep(duration);
}
if (time_travel_mode != TT_MODE_OFF)
time_travel_sleep();
else
os_idle_sleep();
}
void arch_cpu_idle(void)
......
......@@ -25,7 +25,7 @@ int write_sigio_irq(int fd)
err = um_request_irq(SIGIO_WRITE_IRQ, fd, IRQ_READ, sigio_interrupt,
0, "write sigio", NULL);
if (err) {
if (err < 0) {
printk(KERN_ERR "write_sigio_irq : um_request_irq failed, "
"err = %d\n", err);
return -1;
......
......@@ -31,6 +31,7 @@ static bool time_travel_start_set;
static unsigned long long time_travel_start;
static unsigned long long time_travel_time;
static LIST_HEAD(time_travel_events);
static LIST_HEAD(time_travel_irqs);
static unsigned long long time_travel_timer_interval;
static unsigned long long time_travel_next_event;
static struct time_travel_event time_travel_timer_event;
......@@ -46,6 +47,9 @@ static void time_travel_set_time(unsigned long long ns)
if (unlikely(ns < time_travel_time))
panic("time-travel: time goes backwards %lld -> %lld\n",
time_travel_time, ns);
else if (unlikely(ns >= S64_MAX))
panic("The system was going to sleep forever, aborting");
time_travel_time = ns;
}
......@@ -180,6 +184,14 @@ static void time_travel_ext_update_request(unsigned long long time)
time == time_travel_ext_prev_request)
return;
/*
* if we're running and are allowed to run past the request
* then we don't need to update it either
*/
if (!time_travel_ext_waiting && time_travel_ext_free_until_valid &&
time < time_travel_ext_free_until)
return;
time_travel_ext_prev_request = time;
time_travel_ext_prev_request_valid = true;
time_travel_ext_req(UM_TIMETRAVEL_REQUEST, time);
......@@ -187,7 +199,13 @@ static void time_travel_ext_update_request(unsigned long long time)
void __time_travel_propagate_time(void)
{
static unsigned long long last_propagated;
if (last_propagated == time_travel_time)
return;
time_travel_ext_req(UM_TIMETRAVEL_UPDATE, time_travel_time);
last_propagated = time_travel_time;
}
EXPORT_SYMBOL_GPL(__time_travel_propagate_time);
......@@ -214,6 +232,7 @@ static void time_travel_ext_wait(bool idle)
};
time_travel_ext_prev_request_valid = false;
time_travel_ext_free_until_valid = false;
time_travel_ext_waiting++;
time_travel_ext_req(UM_TIMETRAVEL_WAIT, -1);
......@@ -260,11 +279,6 @@ static void __time_travel_add_event(struct time_travel_event *e,
struct time_travel_event *tmp;
bool inserted = false;
if (WARN(time_travel_mode == TT_MODE_BASIC &&
e != &time_travel_timer_event,
"only timer events can be handled in basic mode"))
return;
if (e->pending)
return;
......@@ -311,6 +325,35 @@ void time_travel_periodic_timer(struct time_travel_event *e)
deliver_alarm();
}
void deliver_time_travel_irqs(void)
{
struct time_travel_event *e;
unsigned long flags;
/*
* Don't do anything for most cases. Note that because here we have
* to disable IRQs (and re-enable later) we'll actually recurse at
* the end of the function, so this is strictly necessary.
*/
if (likely(list_empty(&time_travel_irqs)))
return;
local_irq_save(flags);
irq_enter();
while ((e = list_first_entry_or_null(&time_travel_irqs,
struct time_travel_event,
list))) {
WARN(e->time != time_travel_time,
"time moved from %lld to %lld before IRQ delivery\n",
time_travel_time, e->time);
list_del(&e->list);
e->pending = false;
e->fn(e);
}
irq_exit();
local_irq_restore(flags);
}
static void time_travel_deliver_event(struct time_travel_event *e)
{
if (e == &time_travel_timer_event) {
......@@ -319,6 +362,14 @@ static void time_travel_deliver_event(struct time_travel_event *e)
* by itself, so must handle it specially here
*/
e->fn(e);
} else if (irqs_disabled()) {
list_add_tail(&e->list, &time_travel_irqs);
/*
* set pending again, it was set to false when the
* event was deleted from the original list, but
* now it's still pending until we deliver the IRQ.
*/
e->pending = true;
} else {
unsigned long flags;
......@@ -404,9 +455,14 @@ static void time_travel_oneshot_timer(struct time_travel_event *e)
deliver_alarm();
}
void time_travel_sleep(unsigned long long duration)
void time_travel_sleep(void)
{
unsigned long long next = time_travel_time + duration;
/*
* Wait "forever" (using S64_MAX because there are some potential
* wrapping issues, especially with the current TT_MODE_EXTERNAL
* controller application.
*/
unsigned long long next = S64_MAX;
if (time_travel_mode == TT_MODE_BASIC)
os_timer_disable();
......@@ -483,6 +539,7 @@ static int time_travel_connect_external(const char *socket)
#define time_travel_start_set 0
#define time_travel_start 0
#define time_travel_time 0
#define time_travel_ext_waiting 0
static inline void time_travel_update_time(unsigned long long ns, bool retearly)
{
......@@ -628,7 +685,8 @@ static u64 timer_read(struct clocksource *cs)
* "what do I do next" and onstack event we use to know when
* to return from time_travel_update_time().
*/
if (!irqs_disabled() && !in_interrupt() && !in_softirq())
if (!irqs_disabled() && !in_interrupt() && !in_softirq() &&
!time_travel_ext_waiting)
time_travel_update_time(time_travel_time +
TIMER_MULTIPLIER,
false);
......@@ -673,10 +731,8 @@ void read_persistent_clock64(struct timespec64 *ts)
{
long long nsecs;
if (time_travel_start_set)
if (time_travel_mode != TT_MODE_OFF)
nsecs = time_travel_start + time_travel_time;
else if (time_travel_mode == TT_MODE_EXTERNAL)
nsecs = time_travel_ext_req(UM_TIMETRAVEL_GET_TOD, -1);
else
nsecs = os_persistent_clock_emulation();
......@@ -686,6 +742,25 @@ void read_persistent_clock64(struct timespec64 *ts)
void __init time_init(void)
{
#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
switch (time_travel_mode) {
case TT_MODE_EXTERNAL:
time_travel_start = time_travel_ext_req(UM_TIMETRAVEL_GET_TOD, -1);
/* controller gave us the *current* time, so adjust by that */
time_travel_ext_get_time();
time_travel_start -= time_travel_time;
break;
case TT_MODE_INFCPU:
case TT_MODE_BASIC:
if (!time_travel_start_set)
time_travel_start = os_persistent_clock_emulation();
break;
case TT_MODE_OFF:
/* we just read the host clock with os_persistent_clock_emulation() */
break;
}
#endif
timer_set_signal_handler();
late_time_init = um_timer_setup;
}
......
......@@ -608,3 +608,57 @@ void force_flush_all(void)
vma = vma->vm_next;
}
}
struct page_change_data {
unsigned int set_mask, clear_mask;
};
static int change_page_range(pte_t *ptep, unsigned long addr, void *data)
{
struct page_change_data *cdata = data;
pte_t pte = READ_ONCE(*ptep);
pte_clear_bits(pte, cdata->clear_mask);
pte_set_bits(pte, cdata->set_mask);
set_pte(ptep, pte);
return 0;
}
static int change_memory(unsigned long start, unsigned long pages,
unsigned int set_mask, unsigned int clear_mask)
{
unsigned long size = pages * PAGE_SIZE;
struct page_change_data data;
int ret;
data.set_mask = set_mask;
data.clear_mask = clear_mask;
ret = apply_to_page_range(&init_mm, start, size, change_page_range,
&data);
flush_tlb_kernel_range(start, start + size);
return ret;
}
int set_memory_ro(unsigned long addr, int numpages)
{
return change_memory(addr, numpages, 0, _PAGE_RW);
}
int set_memory_rw(unsigned long addr, int numpages)
{
return change_memory(addr, numpages, _PAGE_RW, 0);
}
int set_memory_nx(unsigned long addr, int numpages)
{
return -EOPNOTSUPP;
}
int set_memory_x(unsigned long addr, int numpages)
{
return -EOPNOTSUPP;
}
......@@ -13,6 +13,7 @@
#include <linux/sched.h>
#include <linux/sched/task.h>
#include <linux/kmsg_dump.h>
#include <linux/suspend.h>
#include <asm/processor.h>
#include <asm/sections.h>
......@@ -377,3 +378,69 @@ void *text_poke(void *addr, const void *opcode, size_t len)
void text_poke_sync(void)
{
}
void uml_pm_wake(void)
{
pm_system_wakeup();
}
#ifdef CONFIG_PM_SLEEP
static int um_suspend_valid(suspend_state_t state)
{
return state == PM_SUSPEND_MEM;
}
static int um_suspend_prepare(void)
{
um_irqs_suspend();
return 0;
}
static int um_suspend_enter(suspend_state_t state)
{
if (WARN_ON(state != PM_SUSPEND_MEM))
return -EINVAL;
/*
* This is identical to the idle sleep, but we've just
* (during suspend) turned off all interrupt sources
* except for the ones we want, so now we can only wake
* up on something we actually want to wake up on. All
* timing has also been suspended.
*/
um_idle_sleep();
return 0;
}
static void um_suspend_finish(void)
{
um_irqs_resume();
}
const struct platform_suspend_ops um_suspend_ops = {
.valid = um_suspend_valid,
.prepare = um_suspend_prepare,
.enter = um_suspend_enter,
.finish = um_suspend_finish,
};
static int init_pm_wake_signal(void)
{
/*
* In external time-travel mode we can't use signals to wake up
* since that would mess with the scheduling. We'll have to do
* some additional work to support wakeup on virtio devices or
* similar, perhaps implementing a fake RTC controller that can
* trigger wakeup (and request the appropriate scheduling from
* the external scheduler when going to suspend.)
*/
if (time_travel_mode != TT_MODE_EXTERNAL)
register_pm_wake_signal();
suspend_set_ops(&um_suspend_ops);
return 0;
}
late_initcall(init_pm_wake_signal);
#endif
......@@ -10,6 +10,8 @@ obj-y = execvp.o file.o helper.o irq.o main.o mem.o process.o \
registers.o sigio.o signal.o start_up.o time.o tty.o \
umid.o user_syms.o util.o drivers/ skas/
CFLAGS_signal.o += -Wframe-larger-than=4096
obj-$(CONFIG_ARCH_REUSE_HOST_VSYSCALL_AREA) += elf_aux.o
USER_OBJS := $(user-objs-y) elf_aux.o execvp.o file.o helper.o irq.o \
......
......@@ -45,7 +45,7 @@ int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv)
unsigned long stack, sp;
int pid, fds[2], ret, n;
stack = alloc_stack(0, __cant_sleep());
stack = alloc_stack(__cant_sleep());
if (stack == 0)
return -ENOMEM;
......@@ -116,7 +116,7 @@ int run_helper_thread(int (*proc)(void *), void *arg, unsigned int flags,
unsigned long stack, sp;
int pid, status, err;
stack = alloc_stack(0, __cant_sleep());
stack = alloc_stack(__cant_sleep());
if (stack == 0)
return -ENOMEM;
......
......@@ -45,10 +45,10 @@ int os_epoll_triggered(int index, int events)
* access to the right includes/defines for EPOLL constants.
*/
int os_event_mask(int irq_type)
int os_event_mask(enum um_irq_type irq_type)
{
if (irq_type == IRQ_READ)
return EPOLLIN | EPOLLPRI;
return EPOLLIN | EPOLLPRI | EPOLLERR | EPOLLHUP | EPOLLRDHUP;
if (irq_type == IRQ_WRITE)
return EPOLLOUT;
return 0;
......
......@@ -164,45 +164,55 @@ static void update_thread(void)
set_signals_trace(flags);
}
int add_sigio_fd(int fd)
int __add_sigio_fd(int fd)
{
struct pollfd *p;
int err = 0, i, n;
int err, i, n;
sigio_lock();
for (i = 0; i < all_sigio_fds.used; i++) {
if (all_sigio_fds.poll[i].fd == fd)
break;
}
if (i == all_sigio_fds.used)
goto out;
return -ENOSPC;
p = &all_sigio_fds.poll[i];
for (i = 0; i < current_poll.used; i++) {
if (current_poll.poll[i].fd == fd)
goto out;
return 0;
}
n = current_poll.used;
err = need_poll(&next_poll, n + 1);
if (err)
goto out;
return err;
memcpy(next_poll.poll, current_poll.poll,
current_poll.used * sizeof(struct pollfd));
next_poll.poll[n] = *p;
next_poll.used = n + 1;
update_thread();
out:
return 0;
}
int add_sigio_fd(int fd)
{
int err;
sigio_lock();
err = __add_sigio_fd(fd);
sigio_unlock();
return err;
}
int ignore_sigio_fd(int fd)
int __ignore_sigio_fd(int fd)
{
struct pollfd *p;
int err = 0, i, n = 0;
int err, i, n = 0;
/*
* This is called from exitcalls elsewhere in UML - if
......@@ -212,17 +222,16 @@ int ignore_sigio_fd(int fd)
if (write_sigio_pid == -1)
return -EIO;
sigio_lock();
for (i = 0; i < current_poll.used; i++) {
if (current_poll.poll[i].fd == fd)
break;
}
if (i == current_poll.used)
goto out;
return -ENOENT;
err = need_poll(&next_poll, current_poll.used - 1);
if (err)
goto out;
return err;
for (i = 0; i < current_poll.used; i++) {
p = &current_poll.poll[i];
......@@ -232,8 +241,18 @@ int ignore_sigio_fd(int fd)
next_poll.used = current_poll.used - 1;
update_thread();
out:
return 0;
}
int ignore_sigio_fd(int fd)
{
int err;
sigio_lock();
err = __ignore_sigio_fd(fd);
sigio_unlock();
return err;
}
......@@ -336,7 +355,7 @@ static void write_sigio_workaround(void)
close(l_write_sigio_fds[1]);
}
void sigio_broken(int fd, int read)
void sigio_broken(int fd)
{
int err;
......@@ -352,7 +371,7 @@ void sigio_broken(int fd, int read)
all_sigio_fds.poll[all_sigio_fds.used++] =
((struct pollfd) { .fd = fd,
.events = read ? POLLIN : POLLOUT,
.events = POLLIN,
.revents = 0 });
out:
sigio_unlock();
......@@ -360,17 +379,16 @@ void sigio_broken(int fd, int read)
/* Changed during early boot */
static int pty_output_sigio;
static int pty_close_sigio;
void maybe_sigio_broken(int fd, int read)
void maybe_sigio_broken(int fd)
{
if (!isatty(fd))
return;
if ((read || pty_output_sigio) && (!read || pty_close_sigio))
if (pty_output_sigio)
return;
sigio_broken(fd, read);
sigio_broken(fd);
}
static void sigio_cleanup(void)
......@@ -514,19 +532,6 @@ static void tty_output(int master, int slave)
printk(UM_KERN_CONT "tty_output : read failed, err = %d\n", n);
}
static void tty_close(int master, int slave)
{
printk(UM_KERN_INFO "Checking that host ptys support SIGIO on "
"close...");
close(slave);
if (got_sigio) {
printk(UM_KERN_CONT "Yes\n");
pty_close_sigio = 1;
} else
printk(UM_KERN_CONT "No, enabling workaround\n");
}
static void __init check_sigio(void)
{
if ((access("/dev/ptmx", R_OK) < 0) &&
......@@ -536,7 +541,6 @@ static void __init check_sigio(void)
return;
}
check_one_sigio(tty_output);
check_one_sigio(tty_close);
}
/* Here because it only does the SIGIO testing for now */
......
......@@ -136,6 +136,16 @@ void set_sigstack(void *sig_stack, int size)
panic("enabling signal stack failed, errno = %d\n", errno);
}
static void sigusr1_handler(int sig, struct siginfo *unused_si, mcontext_t *mc)
{
uml_pm_wake();
}
void register_pm_wake_signal(void)
{
set_handler(SIGUSR1);
}
static void (*handlers[_NSIG])(int sig, struct siginfo *si, mcontext_t *mc) = {
[SIGSEGV] = sig_handler,
[SIGBUS] = sig_handler,
......@@ -145,7 +155,9 @@ static void (*handlers[_NSIG])(int sig, struct siginfo *si, mcontext_t *mc) = {
[SIGIO] = sig_handler,
[SIGWINCH] = sig_handler,
[SIGALRM] = timer_alarm_handler
[SIGALRM] = timer_alarm_handler,
[SIGUSR1] = sigusr1_handler,
};
static void hard_handler(int sig, siginfo_t *si, void *p)
......@@ -222,6 +234,11 @@ void set_handler(int sig)
panic("sigprocmask failed - errno = %d\n", errno);
}
void send_sigio_to_self(void)
{
kill(os_getpid(), SIGIO);
}
int change_sig(int signal, int on)
{
sigset_t sigset;
......@@ -254,6 +271,9 @@ void unblock_signals(void)
return;
signals_enabled = 1;
#ifdef UML_CONFIG_UML_TIME_TRAVEL_SUPPORT
deliver_time_travel_irqs();
#endif
/*
* We loop because the IRQ handler returns with interrupts off. So,
......
......@@ -400,7 +400,20 @@ void userspace(struct uml_pt_regs *regs, unsigned long *aux_fp_regs)
if (WIFSTOPPED(status)) {
int sig = WSTOPSIG(status);
ptrace(PTRACE_GETSIGINFO, pid, 0, (struct siginfo *)&si);
/* These signal handlers need the si argument.
* The SIGIO and SIGALARM handlers which constitute the
* majority of invocations, do not use it.
*/
switch (sig) {
case SIGSEGV:
case SIGTRAP:
case SIGILL:
case SIGBUS:
case SIGFPE:
case SIGWINCH:
ptrace(PTRACE_GETSIGINFO, pid, 0, (struct siginfo *)&si);
break;
}
switch (sig) {
case SIGSEGV:
......
......@@ -7,6 +7,7 @@
*/
#include <stddef.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
......@@ -99,19 +100,9 @@ long long os_nsecs(void)
}
/**
* os_idle_sleep() - sleep for a given time of nsecs
* @nsecs: nanoseconds to sleep
* os_idle_sleep() - sleep until interrupted
*/
void os_idle_sleep(unsigned long long nsecs)
void os_idle_sleep(void)
{
struct timespec ts = {
.tv_sec = nsecs / UM_NSEC_PER_SEC,
.tv_nsec = nsecs % UM_NSEC_PER_SEC
};
/*
* Relay the signal if clock_nanosleep is interrupted.
*/
if (clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL))
deliver_alarm();
pause();
}
......@@ -137,20 +137,13 @@ static inline int is_umdir_used(char *dir)
{
char pid[sizeof("nnnnnnnnn")], *end, *file;
int dead, fd, p, n, err;
size_t filelen;
size_t filelen = strlen(dir) + sizeof("/pid") + 1;
err = asprintf(&file, "%s/pid", dir);
if (err < 0)
return 0;
filelen = strlen(file);
file = malloc(filelen);
if (!file)
return -ENOMEM;
n = snprintf(file, filelen, "%s/pid", dir);
if (n >= filelen) {
printk(UM_KERN_ERR "is_umdir_used - pid filename too long\n");
err = -E2BIG;
goto out;
}
snprintf(file, filelen, "%s/pid", dir);
dead = 0;
fd = open(file, O_RDONLY);
......
......@@ -528,15 +528,15 @@ endif # HW_RANDOM
config UML_RANDOM
depends on UML
tristate "Hardware random number generator"
select HW_RANDOM
tristate "UML Random Number Generator support"
help
This option enables UML's "hardware" random number generator. It
attaches itself to the host's /dev/random, supplying as much entropy
as the host has, rather than the small amount the UML gets from its
own drivers. It registers itself as a standard hardware random number
generator, major 10, minor 183, and the canonical device name is
/dev/hwrng.
The way to make use of this is to install the rng-tools package
(check your distro, or download from
http://sourceforge.net/projects/gkernel/). rngd periodically reads
/dev/hwrng and injects the entropy into /dev/random.
own drivers. It registers itself as a rng-core driver thus providing
a device which is usually called /dev/hwrng. This hardware random
number generator does feed into the kernel's random number generator
entropy pool.
If unsure, say Y.
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