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 ...@@ -15,6 +15,7 @@ config UML
select HAVE_DEBUG_KMEMLEAK select HAVE_DEBUG_KMEMLEAK
select HAVE_DEBUG_BUGVERBOSE select HAVE_DEBUG_BUGVERBOSE
select NO_DMA select NO_DMA
select ARCH_HAS_SET_MEMORY
select GENERIC_IRQ_SHOW select GENERIC_IRQ_SHOW
select GENERIC_CPU_DEVICES select GENERIC_CPU_DEVICES
select HAVE_GCC_PLUGINS select HAVE_GCC_PLUGINS
...@@ -191,3 +192,8 @@ config UML_TIME_TRAVEL_SUPPORT ...@@ -191,3 +192,8 @@ config UML_TIME_TRAVEL_SUPPORT
endmenu endmenu
source "arch/um/drivers/Kconfig" 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) ...@@ -26,10 +26,10 @@ int generic_read(int fd, char *c_out, void *unused)
n = read(fd, c_out, sizeof(*c_out)); n = read(fd, c_out, sizeof(*c_out));
if (n > 0) if (n > 0)
return n; return n;
else if (errno == EAGAIN)
return 0;
else if (n == 0) else if (n == 0)
return -EIO; return -EIO;
else if (errno == EAGAIN)
return 0;
return -errno; return -errno;
} }
......
...@@ -262,19 +262,25 @@ static irqreturn_t line_write_interrupt(int irq, void *data) ...@@ -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) int line_setup_irq(int fd, int input, int output, struct line *line, void *data)
{ {
const struct line_driver *driver = line->driver; 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, err = um_request_irq(driver->read_irq, fd, IRQ_READ,
line_interrupt, IRQF_SHARED, line_interrupt, IRQF_SHARED,
driver->read_irq_name, data); driver->read_irq_name, data);
if (err) if (err < 0)
return err; return err;
if (output) }
if (output) {
err = um_request_irq(driver->write_irq, fd, IRQ_WRITE, err = um_request_irq(driver->write_irq, fd, IRQ_WRITE,
line_write_interrupt, IRQF_SHARED, line_write_interrupt, IRQF_SHARED,
driver->write_irq_name, data); driver->write_irq_name, data);
if (err < 0)
return err; return err;
}
return 0;
} }
static int line_activate(struct tty_port *port, struct tty_struct *tty) static int line_activate(struct tty_port *port, struct tty_struct *tty)
...@@ -608,7 +614,6 @@ static void free_winch(struct winch *winch) ...@@ -608,7 +614,6 @@ static void free_winch(struct winch *winch)
winch->fd = -1; winch->fd = -1;
if (fd != -1) if (fd != -1)
os_close_file(fd); os_close_file(fd);
list_del(&winch->list);
__free_winch(&winch->work); __free_winch(&winch->work);
} }
...@@ -709,6 +714,8 @@ static void unregister_winch(struct tty_struct *tty) ...@@ -709,6 +714,8 @@ static void unregister_winch(struct tty_struct *tty)
winch = list_entry(ele, struct winch, list); winch = list_entry(ele, struct winch, list);
wtty = tty_port_tty_get(winch->port); wtty = tty_port_tty_get(winch->port);
if (wtty == tty) { if (wtty == tty) {
list_del(&winch->list);
spin_unlock(&winch_handler_lock);
free_winch(winch); free_winch(winch);
break; break;
} }
...@@ -719,14 +726,17 @@ static void unregister_winch(struct tty_struct *tty) ...@@ -719,14 +726,17 @@ static void unregister_winch(struct tty_struct *tty)
static void winch_cleanup(void) static void winch_cleanup(void)
{ {
struct list_head *ele, *next;
struct winch *winch; struct winch *winch;
spin_lock(&winch_handler_lock); 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); free_winch(winch);
spin_lock(&winch_handler_lock);
} }
spin_unlock(&winch_handler_lock); spin_unlock(&winch_handler_lock);
......
...@@ -738,7 +738,7 @@ static int __init mconsole_init(void) ...@@ -738,7 +738,7 @@ static int __init mconsole_init(void)
err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt, err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
IRQF_SHARED, "mconsole", (void *)sock); IRQF_SHARED, "mconsole", (void *)sock);
if (err) { if (err < 0) {
printk(KERN_ERR "Failed to get IRQ for management console\n"); printk(KERN_ERR "Failed to get IRQ for management console\n");
goto out; goto out;
} }
......
...@@ -160,7 +160,7 @@ static int uml_net_open(struct net_device *dev) ...@@ -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, err = um_request_irq(dev->irq, lp->fd, IRQ_READ, uml_net_interrupt,
IRQF_SHARED, dev->name, dev); IRQF_SHARED, dev->name, dev);
if (err != 0) { if (err < 0) {
printk(KERN_ERR "uml_net_open: failed to get irq(%d)\n", err); printk(KERN_ERR "uml_net_open: failed to get irq(%d)\n", err);
err = -ENETUNREACH; err = -ENETUNREACH;
goto out_close; goto out_close;
......
...@@ -100,7 +100,7 @@ static int port_accept(struct port_list *port) ...@@ -100,7 +100,7 @@ static int port_accept(struct port_list *port)
.port = port }); .port = port });
if (um_request_irq(TELNETD_IRQ, socket[0], IRQ_READ, pipe_interrupt, 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 " printk(KERN_ERR "port_accept : failed to get IRQ for "
"telnetd\n"); "telnetd\n");
goto out_free; goto out_free;
...@@ -182,7 +182,7 @@ void *port_data(int port_num) ...@@ -182,7 +182,7 @@ void *port_data(int port_num)
} }
if (um_request_irq(ACCEPT_IRQ, fd, IRQ_READ, port_interrupt, 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); printk(KERN_ERR "Failed to get IRQ for port %d\n", port_num);
goto out_close; goto out_close;
} }
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/miscdevice.h> #include <linux/miscdevice.h>
#include <linux/hw_random.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <init.h> #include <init.h>
...@@ -18,9 +19,8 @@ ...@@ -18,9 +19,8 @@
#include <os.h> #include <os.h>
/* /*
* core module and version information * core module information
*/ */
#define RNG_VERSION "1.0.0"
#define RNG_MODULE_NAME "hw_random" #define RNG_MODULE_NAME "hw_random"
/* Changed at init time, in the non-modular case, and at module load /* Changed at init time, in the non-modular case, and at module load
...@@ -28,88 +28,36 @@ ...@@ -28,88 +28,36 @@
* protects against a module being loaded twice at the same time. * protects against a module being loaded twice at the same time.
*/ */
static int random_fd = -1; 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 */ int ret;
if ((filp->f_mode & FMODE_READ) == 0)
return -EINVAL;
if ((filp->f_mode & FMODE_WRITE) != 0)
return -EINVAL;
return 0; for (;;) {
} ret = os_read_file(random_fd, buf, max);
if (block && ret == -EAGAIN) {
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);
add_sigio_fd(random_fd); add_sigio_fd(random_fd);
add_wait_queue(&host_read_wait, &wait); ret = wait_for_completion_killable(&have_data);
set_current_state(TASK_INTERRUPTIBLE);
schedule();
remove_wait_queue(&host_read_wait, &wait);
if (atomic_dec_and_test(&host_sleep_count)) {
ignore_sigio_fd(random_fd); ignore_sigio_fd(random_fd);
deactivate_fd(random_fd, RANDOM_IRQ); deactivate_fd(random_fd, RANDOM_IRQ);
if (ret < 0)
break;
} else {
break;
} }
} }
else
return n;
if (signal_pending (current)) return ret != -EAGAIN ? ret : 0;
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,
};
static irqreturn_t random_interrupt(int irq, void *data) static irqreturn_t random_interrupt(int irq, void *data)
{ {
wake_up(&host_read_wait); complete(&have_data);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -126,18 +74,19 @@ static int __init rng_init (void) ...@@ -126,18 +74,19 @@ static int __init rng_init (void)
goto out; goto out;
random_fd = err; random_fd = err;
err = um_request_irq(RANDOM_IRQ, random_fd, IRQ_READ, random_interrupt, err = um_request_irq(RANDOM_IRQ, random_fd, IRQ_READ, random_interrupt,
0, "random", NULL); 0, "random", NULL);
if (err) if (err < 0)
goto err_out_cleanup_hw; 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) { if (err) {
printk (KERN_ERR RNG_MODULE_NAME ": misc device register " pr_err(RNG_MODULE_NAME " registering failed (%d)\n", err);
"failed\n");
goto err_out_cleanup_hw; goto err_out_cleanup_hw;
} }
out: out:
...@@ -161,8 +110,8 @@ static void cleanup(void) ...@@ -161,8 +110,8 @@ static void cleanup(void)
static void __exit rng_cleanup(void) static void __exit rng_cleanup(void)
{ {
hwrng_unregister(&hwrng);
os_close_file(random_fd); os_close_file(random_fd);
misc_deregister (&rng_miscdev);
} }
module_init (rng_init); module_init (rng_init);
......
This diff is collapsed.
...@@ -1196,9 +1196,9 @@ static int vector_net_close(struct net_device *dev) ...@@ -1196,9 +1196,9 @@ static int vector_net_close(struct net_device *dev)
/* TX tasklet */ /* 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++; vp->estats.tx_kicks++;
vector_send(vp->tx_queue); vector_send(vp->tx_queue);
...@@ -1271,7 +1271,7 @@ static int vector_net_open(struct net_device *dev) ...@@ -1271,7 +1271,7 @@ static int vector_net_open(struct net_device *dev)
irq_rr + VECTOR_BASE_IRQ, vp->fds->rx_fd, irq_rr + VECTOR_BASE_IRQ, vp->fds->rx_fd,
IRQ_READ, vector_rx_interrupt, IRQ_READ, vector_rx_interrupt,
IRQF_SHARED, dev->name, dev); IRQF_SHARED, dev->name, dev);
if (err != 0) { if (err < 0) {
netdev_err(dev, "vector_open: failed to get rx irq(%d)\n", err); netdev_err(dev, "vector_open: failed to get rx irq(%d)\n", err);
err = -ENETUNREACH; err = -ENETUNREACH;
goto out_close; goto out_close;
...@@ -1286,7 +1286,7 @@ static int vector_net_open(struct net_device *dev) ...@@ -1286,7 +1286,7 @@ static int vector_net_open(struct net_device *dev)
irq_rr + VECTOR_BASE_IRQ, vp->fds->tx_fd, irq_rr + VECTOR_BASE_IRQ, vp->fds->tx_fd,
IRQ_WRITE, vector_tx_interrupt, IRQ_WRITE, vector_tx_interrupt,
IRQF_SHARED, dev->name, dev); IRQF_SHARED, dev->name, dev);
if (err != 0) { if (err < 0) {
netdev_err(dev, netdev_err(dev,
"vector_open: failed to get tx irq(%d)\n", err); "vector_open: failed to get tx irq(%d)\n", err);
err = -ENETUNREACH; err = -ENETUNREACH;
...@@ -1629,7 +1629,7 @@ static void vector_eth_configure( ...@@ -1629,7 +1629,7 @@ static void vector_eth_configure(
}); });
dev->features = dev->hw_features = (NETIF_F_SG | NETIF_F_FRAGLIST); 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); INIT_WORK(&vp->reset_tx, vector_reset_tx);
timer_setup(&vp->tl, vector_timer_expire, 0); timer_setup(&vp->tl, vector_timer_expire, 0);
......
...@@ -33,11 +33,6 @@ ...@@ -33,11 +33,6 @@
#include <os.h> #include <os.h>
#include "vhost_user.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 MAX_SUPPORTED_QUEUE_SIZE 256
#define to_virtio_uml_device(_vdev) \ #define to_virtio_uml_device(_vdev) \
...@@ -55,7 +50,7 @@ struct virtio_uml_device { ...@@ -55,7 +50,7 @@ struct virtio_uml_device {
struct platform_device *pdev; struct platform_device *pdev;
spinlock_t sock_lock; spinlock_t sock_lock;
int sock, req_fd; int sock, req_fd, irq;
u64 features; u64 features;
u64 protocol_features; u64 protocol_features;
u8 status; u8 status;
...@@ -409,12 +404,14 @@ static int vhost_user_init_slave_req(struct virtio_uml_device *vu_dev) ...@@ -409,12 +404,14 @@ static int vhost_user_init_slave_req(struct virtio_uml_device *vu_dev)
return rc; return rc;
vu_dev->req_fd = req_fds[0]; 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_req_interrupt, IRQF_SHARED,
vu_dev->pdev->name, vu_dev); vu_dev->pdev->name, vu_dev);
if (rc) if (rc < 0)
goto err_close; goto err_close;
vu_dev->irq = rc;
rc = vhost_user_send_no_payload_fd(vu_dev, VHOST_USER_SET_SLAVE_REQ_FD, rc = vhost_user_send_no_payload_fd(vu_dev, VHOST_USER_SET_SLAVE_REQ_FD,
req_fds[1]); req_fds[1]);
if (rc) if (rc)
...@@ -423,7 +420,7 @@ static int vhost_user_init_slave_req(struct virtio_uml_device *vu_dev) ...@@ -423,7 +420,7 @@ static int vhost_user_init_slave_req(struct virtio_uml_device *vu_dev)
goto out; goto out;
err_free_irq: err_free_irq:
um_free_irq(VIRTIO_IRQ, vu_dev); um_free_irq(vu_dev->irq, vu_dev);
err_close: err_close:
os_close_file(req_fds[0]); os_close_file(req_fds[0]);
out: out:
...@@ -802,7 +799,11 @@ static void vu_del_vq(struct virtqueue *vq) ...@@ -802,7 +799,11 @@ static void vu_del_vq(struct virtqueue *vq)
struct virtio_uml_vq_info *info = vq->priv; struct virtio_uml_vq_info *info = vq->priv;
if (info->call_fd >= 0) { 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); os_close_file(info->call_fd);
} }
...@@ -852,9 +853,9 @@ static int vu_setup_vq_call_fd(struct virtio_uml_device *vu_dev, ...@@ -852,9 +853,9 @@ static int vu_setup_vq_call_fd(struct virtio_uml_device *vu_dev,
return rc; return rc;
info->call_fd = call_fds[0]; 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); vu_interrupt, IRQF_SHARED, info->name, vq);
if (rc) if (rc < 0)
goto close_both; goto close_both;
rc = vhost_user_set_vring_call(vu_dev, vq->index, call_fds[1]); 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, ...@@ -864,7 +865,7 @@ static int vu_setup_vq_call_fd(struct virtio_uml_device *vu_dev,
goto out; goto out;
release_irq: release_irq:
um_free_irq(VIRTIO_IRQ, vq); um_free_irq(vu_dev->irq, vq);
close_both: close_both:
os_close_file(call_fds[0]); os_close_file(call_fds[0]);
out: out:
...@@ -969,7 +970,7 @@ static struct virtqueue *vu_setup_vq(struct virtio_device *vdev, ...@@ -969,7 +970,7 @@ static struct virtqueue *vu_setup_vq(struct virtio_device *vdev,
error_setup: error_setup:
if (info->call_fd >= 0) { if (info->call_fd >= 0) {
um_free_irq(VIRTIO_IRQ, vq); um_free_irq(vu_dev->irq, vq);
os_close_file(info->call_fd); os_close_file(info->call_fd);
} }
error_call: error_call:
...@@ -1078,7 +1079,7 @@ static void virtio_uml_release_dev(struct device *d) ...@@ -1078,7 +1079,7 @@ static void virtio_uml_release_dev(struct device *d)
/* might not have been opened due to not negotiating the feature */ /* might not have been opened due to not negotiating the feature */
if (vu_dev->req_fd >= 0) { 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); os_close_file(vu_dev->req_fd);
} }
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
struct xterm_chan { struct xterm_chan {
int pid; int pid;
int helper_pid; int helper_pid;
int chan_fd;
char *title; char *title;
int device; int device;
int raw; int raw;
...@@ -33,6 +34,7 @@ static void *xterm_init(char *str, int device, const struct chan_opts *opts) ...@@ -33,6 +34,7 @@ static void *xterm_init(char *str, int device, const struct chan_opts *opts)
return NULL; return NULL;
*data = ((struct xterm_chan) { .pid = -1, *data = ((struct xterm_chan) { .pid = -1,
.helper_pid = -1, .helper_pid = -1,
.chan_fd = -1,
.device = device, .device = device,
.title = opts->xterm_title, .title = opts->xterm_title,
.raw = opts->raw } ); .raw = opts->raw } );
...@@ -149,6 +151,7 @@ static int xterm_open(int input, int output, int primary, void *d, ...@@ -149,6 +151,7 @@ static int xterm_open(int input, int output, int primary, void *d,
goto out_kill; goto out_kill;
} }
data->chan_fd = fd;
new = xterm_fd(fd, &data->helper_pid); new = xterm_fd(fd, &data->helper_pid);
if (new < 0) { if (new < 0) {
err = new; err = new;
...@@ -206,6 +209,8 @@ static void xterm_close(int fd, void *d) ...@@ -206,6 +209,8 @@ static void xterm_close(int fd, void *d)
os_kill_process(data->helper_pid, 0); os_kill_process(data->helper_pid, 0);
data->helper_pid = -1; data->helper_pid = -1;
if (data->chan_fd != -1)
os_close_file(data->chan_fd);
os_close_file(fd); os_close_file(fd);
} }
......
...@@ -51,7 +51,7 @@ int xterm_fd(int socket, int *pid_out) ...@@ -51,7 +51,7 @@ int xterm_fd(int socket, int *pid_out)
err = um_request_irq(XTERM_IRQ, socket, IRQ_READ, xterm_interrupt, err = um_request_irq(XTERM_IRQ, socket, IRQ_READ, xterm_interrupt,
IRQF_SHARED, "xterm", data); IRQF_SHARED, "xterm", data);
if (err) { if (err < 0) {
printk(KERN_ERR "xterm_fd : failed to get IRQ for xterm, " printk(KERN_ERR "xterm_fd : failed to get IRQ for xterm, "
"err = %d\n", err); "err = %d\n", err);
ret = err; ret = err;
......
...@@ -17,21 +17,20 @@ ...@@ -17,21 +17,20 @@
#define TELNETD_IRQ 12 #define TELNETD_IRQ 12
#define XTERM_IRQ 13 #define XTERM_IRQ 13
#define RANDOM_IRQ 14 #define RANDOM_IRQ 14
#define VIRTIO_IRQ 15
#ifdef CONFIG_UML_NET_VECTOR #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 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 #else
#define LAST_IRQ VIRTIO_IRQ #define UM_FIRST_DYN_IRQ (RANDOM_IRQ + 1)
#endif #endif
#define NR_IRQS (LAST_IRQ + 1) #define NR_IRQS 64
#endif #endif
...@@ -55,12 +55,15 @@ extern unsigned long end_iomem; ...@@ -55,12 +55,15 @@ extern unsigned long end_iomem;
#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY) #define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY)
#define __PAGE_KERNEL_EXEC \ #define __PAGE_KERNEL_EXEC \
(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED) (_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_NONE __pgprot(_PAGE_PROTNONE | _PAGE_ACCESSED)
#define PAGE_SHARED __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _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_COPY __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED)
#define PAGE_READONLY __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 __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED)
#define PAGE_KERNEL_EXEC __pgprot(__PAGE_KERNEL_EXEC) #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 * 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 { ...@@ -28,7 +28,7 @@ struct time_travel_event {
extern enum time_travel_mode time_travel_mode; extern enum time_travel_mode time_travel_mode;
void time_travel_sleep(unsigned long long duration); void time_travel_sleep(void);
static inline void static inline void
time_travel_set_event_fn(struct time_travel_event *e, time_travel_set_event_fn(struct time_travel_event *e,
...@@ -60,7 +60,7 @@ struct time_travel_event { ...@@ -60,7 +60,7 @@ struct time_travel_event {
#define time_travel_mode TT_MODE_OFF #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); ...@@ -40,3 +40,6 @@ DEFINE(UML_CONFIG_UML_X86, CONFIG_UML_X86);
#ifdef CONFIG_64BIT #ifdef CONFIG_64BIT
DEFINE(UML_CONFIG_64BIT, CONFIG_64BIT); DEFINE(UML_CONFIG_64BIT, CONFIG_64BIT);
#endif #endif
#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
DEFINE(UML_CONFIG_UML_TIME_TRAVEL_SUPPORT, CONFIG_UML_TIME_TRAVEL_SUPPORT);
#endif
...@@ -8,11 +8,12 @@ ...@@ -8,11 +8,12 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <asm/ptrace.h> #include <asm/ptrace.h>
#include "irq_user.h"
extern int um_request_irq(unsigned int irq, int fd, int type, #define UM_IRQ_ALLOC -1
irq_handler_t handler,
unsigned long irqflags, const char * devname,
void *dev_id);
void um_free_irq(unsigned int irq, void *dev);
#endif
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 @@ ...@@ -9,25 +9,12 @@
#include <sysdep/ptrace.h> #include <sysdep/ptrace.h>
#include <stdbool.h> #include <stdbool.h>
struct irq_fd { enum um_irq_type {
struct irq_fd *next; IRQ_READ,
void *id; IRQ_WRITE,
int fd; NUM_IRQ_TYPES,
int type;
int irq;
int events;
bool active;
bool pending;
bool purge;
}; };
#define IRQ_READ 0
#define IRQ_WRITE 1
#define IRQ_NONE 2
#define MAX_IRQ_TYPE (IRQ_NONE + 1)
struct siginfo; struct siginfo;
extern void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs); extern void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs);
extern void free_irq_by_fd(int fd); extern void free_irq_by_fd(int fd);
......
...@@ -19,7 +19,7 @@ extern int kmalloc_ok; ...@@ -19,7 +19,7 @@ extern int kmalloc_ok;
#define UML_ROUND_UP(addr) \ #define UML_ROUND_UP(addr) \
((((unsigned long) addr) + PAGE_SIZE - 1) & PAGE_MASK) ((((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); extern void free_stack(unsigned long stack, int order);
struct pt_regs; struct pt_regs;
...@@ -39,6 +39,8 @@ extern int is_syscall(unsigned long addr); ...@@ -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 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 int start_uml(void);
extern void paging_init(void); extern void paging_init(void);
...@@ -66,5 +68,6 @@ extern void bus_handler(int sig, struct siginfo *si, struct uml_pt_regs *regs); ...@@ -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 winch(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs);
extern void fatal_sigsegv(void) __attribute__ ((noreturn)); extern void fatal_sigsegv(void) __attribute__ ((noreturn));
void um_idle_sleep(void);
#endif #endif
...@@ -233,6 +233,7 @@ extern void timer_set_signal_handler(void); ...@@ -233,6 +233,7 @@ extern void timer_set_signal_handler(void);
extern void set_sigstack(void *sig_stack, int size); extern void set_sigstack(void *sig_stack, int size);
extern void remove_sigstack(void); extern void remove_sigstack(void);
extern void set_handler(int sig); extern void set_handler(int sig);
extern void send_sigio_to_self(void);
extern int change_sig(int signal, int on); extern int change_sig(int signal, int on);
extern void block_signals(void); extern void block_signals(void);
extern void unblock_signals(void); extern void unblock_signals(void);
...@@ -241,6 +242,7 @@ extern int set_signals(int enable); ...@@ -241,6 +242,7 @@ extern int set_signals(int enable);
extern int set_signals_trace(int enable); extern int set_signals_trace(int enable);
extern int os_is_signal_stack(void); extern int os_is_signal_stack(void);
extern void deliver_alarm(void); extern void deliver_alarm(void);
extern void register_pm_wake_signal(void);
/* util.c */ /* util.c */
extern void stack_protections(unsigned long address); extern void stack_protections(unsigned long address);
...@@ -256,7 +258,7 @@ extern void os_warn(const char *fmt, ...) ...@@ -256,7 +258,7 @@ extern void os_warn(const char *fmt, ...)
__attribute__ ((format (printf, 1, 2))); __attribute__ ((format (printf, 1, 2)));
/* time.c */ /* 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_create(void);
extern int os_timer_set_interval(unsigned long long nsecs); extern int os_timer_set_interval(unsigned long long nsecs);
extern int os_timer_one_shot(unsigned long long nsecs); extern int os_timer_one_shot(unsigned long long nsecs);
...@@ -299,19 +301,29 @@ extern void reboot_skas(void); ...@@ -299,19 +301,29 @@ extern void reboot_skas(void);
extern int os_waiting_for_events_epoll(void); extern int os_waiting_for_events_epoll(void);
extern void *os_epoll_get_data_pointer(int index); extern void *os_epoll_get_data_pointer(int index);
extern int os_epoll_triggered(int index, int events); 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_setup_epoll(void);
extern int os_add_epoll_fd(int events, int fd, void *data); 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_mod_epoll_fd(int events, int fd, void *data);
extern int os_del_epoll_fd(int fd); extern int os_del_epoll_fd(int fd);
extern void os_set_ioignore(void); extern void os_set_ioignore(void);
extern void os_close_epoll_fd(void); extern void os_close_epoll_fd(void);
extern void um_irqs_suspend(void);
extern void um_irqs_resume(void);
/* sigio.c */ /* sigio.c */
extern int add_sigio_fd(int fd); extern int add_sigio_fd(int fd);
extern int ignore_sigio_fd(int fd); extern int ignore_sigio_fd(int fd);
extern void maybe_sigio_broken(int fd, int read); extern void maybe_sigio_broken(int fd);
extern void sigio_broken(int fd, int read); 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 */ /* prctl.c */
extern int os_arch_prctl(int pid, int option, unsigned long *arg2); extern int os_arch_prctl(int pid, int option, unsigned long *arg2);
...@@ -330,4 +342,7 @@ extern void unblock_signals_trace(void); ...@@ -330,4 +342,7 @@ extern void unblock_signals_trace(void);
extern void um_trace_signals_on(void); extern void um_trace_signals_on(void);
extern void um_trace_signals_off(void); extern void um_trace_signals_off(void);
/* time-travel */
extern void deliver_time_travel_irqs(void);
#endif #endif
This diff is collapsed.
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include <os.h> #include <os.h>
#include <skas.h> #include <skas.h>
#include <linux/time-internal.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 * 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) ...@@ -62,16 +63,18 @@ void free_stack(unsigned long stack, int order)
free_pages(stack, 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; gfp_t flags = GFP_KERNEL;
if (atomic) if (atomic)
flags = GFP_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) static inline void set_current(struct task_struct *task)
...@@ -203,15 +206,12 @@ void initial_thread_cb(void (*proc)(void *), void *arg) ...@@ -203,15 +206,12 @@ void initial_thread_cb(void (*proc)(void *), void *arg)
kmalloc_ok = save_kmalloc_ok; 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();
if (time_travel_mode != TT_MODE_OFF) { else
time_travel_sleep(duration); os_idle_sleep();
} else {
os_idle_sleep(duration);
}
} }
void arch_cpu_idle(void) void arch_cpu_idle(void)
......
...@@ -25,7 +25,7 @@ int write_sigio_irq(int fd) ...@@ -25,7 +25,7 @@ int write_sigio_irq(int fd)
err = um_request_irq(SIGIO_WRITE_IRQ, fd, IRQ_READ, sigio_interrupt, err = um_request_irq(SIGIO_WRITE_IRQ, fd, IRQ_READ, sigio_interrupt,
0, "write sigio", NULL); 0, "write sigio", NULL);
if (err) { if (err < 0) {
printk(KERN_ERR "write_sigio_irq : um_request_irq failed, " printk(KERN_ERR "write_sigio_irq : um_request_irq failed, "
"err = %d\n", err); "err = %d\n", err);
return -1; return -1;
......
...@@ -31,6 +31,7 @@ static bool time_travel_start_set; ...@@ -31,6 +31,7 @@ static bool time_travel_start_set;
static unsigned long long time_travel_start; static unsigned long long time_travel_start;
static unsigned long long time_travel_time; static unsigned long long time_travel_time;
static LIST_HEAD(time_travel_events); 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_timer_interval;
static unsigned long long time_travel_next_event; static unsigned long long time_travel_next_event;
static struct time_travel_event time_travel_timer_event; static struct time_travel_event time_travel_timer_event;
...@@ -46,6 +47,9 @@ static void time_travel_set_time(unsigned long long ns) ...@@ -46,6 +47,9 @@ static void time_travel_set_time(unsigned long long ns)
if (unlikely(ns < time_travel_time)) if (unlikely(ns < time_travel_time))
panic("time-travel: time goes backwards %lld -> %lld\n", panic("time-travel: time goes backwards %lld -> %lld\n",
time_travel_time, ns); time_travel_time, ns);
else if (unlikely(ns >= S64_MAX))
panic("The system was going to sleep forever, aborting");
time_travel_time = ns; time_travel_time = ns;
} }
...@@ -180,6 +184,14 @@ static void time_travel_ext_update_request(unsigned long long time) ...@@ -180,6 +184,14 @@ static void time_travel_ext_update_request(unsigned long long time)
time == time_travel_ext_prev_request) time == time_travel_ext_prev_request)
return; 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 = time;
time_travel_ext_prev_request_valid = true; time_travel_ext_prev_request_valid = true;
time_travel_ext_req(UM_TIMETRAVEL_REQUEST, time); time_travel_ext_req(UM_TIMETRAVEL_REQUEST, time);
...@@ -187,7 +199,13 @@ static void time_travel_ext_update_request(unsigned long long time) ...@@ -187,7 +199,13 @@ static void time_travel_ext_update_request(unsigned long long time)
void __time_travel_propagate_time(void) 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); time_travel_ext_req(UM_TIMETRAVEL_UPDATE, time_travel_time);
last_propagated = time_travel_time;
} }
EXPORT_SYMBOL_GPL(__time_travel_propagate_time); EXPORT_SYMBOL_GPL(__time_travel_propagate_time);
...@@ -214,6 +232,7 @@ static void time_travel_ext_wait(bool idle) ...@@ -214,6 +232,7 @@ static void time_travel_ext_wait(bool idle)
}; };
time_travel_ext_prev_request_valid = false; time_travel_ext_prev_request_valid = false;
time_travel_ext_free_until_valid = false;
time_travel_ext_waiting++; time_travel_ext_waiting++;
time_travel_ext_req(UM_TIMETRAVEL_WAIT, -1); time_travel_ext_req(UM_TIMETRAVEL_WAIT, -1);
...@@ -260,11 +279,6 @@ static void __time_travel_add_event(struct time_travel_event *e, ...@@ -260,11 +279,6 @@ static void __time_travel_add_event(struct time_travel_event *e,
struct time_travel_event *tmp; struct time_travel_event *tmp;
bool inserted = false; 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) if (e->pending)
return; return;
...@@ -311,6 +325,35 @@ void time_travel_periodic_timer(struct time_travel_event *e) ...@@ -311,6 +325,35 @@ void time_travel_periodic_timer(struct time_travel_event *e)
deliver_alarm(); 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) static void time_travel_deliver_event(struct time_travel_event *e)
{ {
if (e == &time_travel_timer_event) { if (e == &time_travel_timer_event) {
...@@ -319,6 +362,14 @@ static void time_travel_deliver_event(struct time_travel_event *e) ...@@ -319,6 +362,14 @@ static void time_travel_deliver_event(struct time_travel_event *e)
* by itself, so must handle it specially here * by itself, so must handle it specially here
*/ */
e->fn(e); 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 { } else {
unsigned long flags; unsigned long flags;
...@@ -404,9 +455,14 @@ static void time_travel_oneshot_timer(struct time_travel_event *e) ...@@ -404,9 +455,14 @@ static void time_travel_oneshot_timer(struct time_travel_event *e)
deliver_alarm(); 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) if (time_travel_mode == TT_MODE_BASIC)
os_timer_disable(); os_timer_disable();
...@@ -483,6 +539,7 @@ static int time_travel_connect_external(const char *socket) ...@@ -483,6 +539,7 @@ static int time_travel_connect_external(const char *socket)
#define time_travel_start_set 0 #define time_travel_start_set 0
#define time_travel_start 0 #define time_travel_start 0
#define time_travel_time 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) static inline void time_travel_update_time(unsigned long long ns, bool retearly)
{ {
...@@ -628,7 +685,8 @@ static u64 timer_read(struct clocksource *cs) ...@@ -628,7 +685,8 @@ static u64 timer_read(struct clocksource *cs)
* "what do I do next" and onstack event we use to know when * "what do I do next" and onstack event we use to know when
* to return from time_travel_update_time(). * 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 + time_travel_update_time(time_travel_time +
TIMER_MULTIPLIER, TIMER_MULTIPLIER,
false); false);
...@@ -673,10 +731,8 @@ void read_persistent_clock64(struct timespec64 *ts) ...@@ -673,10 +731,8 @@ void read_persistent_clock64(struct timespec64 *ts)
{ {
long long nsecs; long long nsecs;
if (time_travel_start_set) if (time_travel_mode != TT_MODE_OFF)
nsecs = time_travel_start + time_travel_time; 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 else
nsecs = os_persistent_clock_emulation(); nsecs = os_persistent_clock_emulation();
...@@ -686,6 +742,25 @@ void read_persistent_clock64(struct timespec64 *ts) ...@@ -686,6 +742,25 @@ void read_persistent_clock64(struct timespec64 *ts)
void __init time_init(void) 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(); timer_set_signal_handler();
late_time_init = um_timer_setup; late_time_init = um_timer_setup;
} }
......
...@@ -608,3 +608,57 @@ void force_flush_all(void) ...@@ -608,3 +608,57 @@ void force_flush_all(void)
vma = vma->vm_next; 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 @@ ...@@ -13,6 +13,7 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/sched/task.h> #include <linux/sched/task.h>
#include <linux/kmsg_dump.h> #include <linux/kmsg_dump.h>
#include <linux/suspend.h>
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/sections.h> #include <asm/sections.h>
...@@ -377,3 +378,69 @@ void *text_poke(void *addr, const void *opcode, size_t len) ...@@ -377,3 +378,69 @@ void *text_poke(void *addr, const void *opcode, size_t len)
void text_poke_sync(void) 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 \ ...@@ -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 \ registers.o sigio.o signal.o start_up.o time.o tty.o \
umid.o user_syms.o util.o drivers/ skas/ 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 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 \ 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) ...@@ -45,7 +45,7 @@ int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv)
unsigned long stack, sp; unsigned long stack, sp;
int pid, fds[2], ret, n; int pid, fds[2], ret, n;
stack = alloc_stack(0, __cant_sleep()); stack = alloc_stack(__cant_sleep());
if (stack == 0) if (stack == 0)
return -ENOMEM; return -ENOMEM;
...@@ -116,7 +116,7 @@ int run_helper_thread(int (*proc)(void *), void *arg, unsigned int flags, ...@@ -116,7 +116,7 @@ int run_helper_thread(int (*proc)(void *), void *arg, unsigned int flags,
unsigned long stack, sp; unsigned long stack, sp;
int pid, status, err; int pid, status, err;
stack = alloc_stack(0, __cant_sleep()); stack = alloc_stack(__cant_sleep());
if (stack == 0) if (stack == 0)
return -ENOMEM; return -ENOMEM;
......
...@@ -45,10 +45,10 @@ int os_epoll_triggered(int index, int events) ...@@ -45,10 +45,10 @@ int os_epoll_triggered(int index, int events)
* access to the right includes/defines for EPOLL constants. * 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) if (irq_type == IRQ_READ)
return EPOLLIN | EPOLLPRI; return EPOLLIN | EPOLLPRI | EPOLLERR | EPOLLHUP | EPOLLRDHUP;
if (irq_type == IRQ_WRITE) if (irq_type == IRQ_WRITE)
return EPOLLOUT; return EPOLLOUT;
return 0; return 0;
......
...@@ -164,45 +164,55 @@ static void update_thread(void) ...@@ -164,45 +164,55 @@ static void update_thread(void)
set_signals_trace(flags); set_signals_trace(flags);
} }
int add_sigio_fd(int fd) int __add_sigio_fd(int fd)
{ {
struct pollfd *p; struct pollfd *p;
int err = 0, i, n; int err, i, n;
sigio_lock();
for (i = 0; i < all_sigio_fds.used; i++) { for (i = 0; i < all_sigio_fds.used; i++) {
if (all_sigio_fds.poll[i].fd == fd) if (all_sigio_fds.poll[i].fd == fd)
break; break;
} }
if (i == all_sigio_fds.used) if (i == all_sigio_fds.used)
goto out; return -ENOSPC;
p = &all_sigio_fds.poll[i]; p = &all_sigio_fds.poll[i];
for (i = 0; i < current_poll.used; i++) { for (i = 0; i < current_poll.used; i++) {
if (current_poll.poll[i].fd == fd) if (current_poll.poll[i].fd == fd)
goto out; return 0;
} }
n = current_poll.used; n = current_poll.used;
err = need_poll(&next_poll, n + 1); err = need_poll(&next_poll, n + 1);
if (err) if (err)
goto out; return err;
memcpy(next_poll.poll, current_poll.poll, memcpy(next_poll.poll, current_poll.poll,
current_poll.used * sizeof(struct pollfd)); current_poll.used * sizeof(struct pollfd));
next_poll.poll[n] = *p; next_poll.poll[n] = *p;
next_poll.used = n + 1; next_poll.used = n + 1;
update_thread(); update_thread();
out:
return 0;
}
int add_sigio_fd(int fd)
{
int err;
sigio_lock();
err = __add_sigio_fd(fd);
sigio_unlock(); sigio_unlock();
return err; return err;
} }
int ignore_sigio_fd(int fd) int __ignore_sigio_fd(int fd)
{ {
struct pollfd *p; struct pollfd *p;
int err = 0, i, n = 0; int err, i, n = 0;
/* /*
* This is called from exitcalls elsewhere in UML - if * This is called from exitcalls elsewhere in UML - if
...@@ -212,17 +222,16 @@ int ignore_sigio_fd(int fd) ...@@ -212,17 +222,16 @@ int ignore_sigio_fd(int fd)
if (write_sigio_pid == -1) if (write_sigio_pid == -1)
return -EIO; return -EIO;
sigio_lock();
for (i = 0; i < current_poll.used; i++) { for (i = 0; i < current_poll.used; i++) {
if (current_poll.poll[i].fd == fd) if (current_poll.poll[i].fd == fd)
break; break;
} }
if (i == current_poll.used) if (i == current_poll.used)
goto out; return -ENOENT;
err = need_poll(&next_poll, current_poll.used - 1); err = need_poll(&next_poll, current_poll.used - 1);
if (err) if (err)
goto out; return err;
for (i = 0; i < current_poll.used; i++) { for (i = 0; i < current_poll.used; i++) {
p = &current_poll.poll[i]; p = &current_poll.poll[i];
...@@ -232,8 +241,18 @@ int ignore_sigio_fd(int fd) ...@@ -232,8 +241,18 @@ int ignore_sigio_fd(int fd)
next_poll.used = current_poll.used - 1; next_poll.used = current_poll.used - 1;
update_thread(); update_thread();
out:
return 0;
}
int ignore_sigio_fd(int fd)
{
int err;
sigio_lock();
err = __ignore_sigio_fd(fd);
sigio_unlock(); sigio_unlock();
return err; return err;
} }
...@@ -336,7 +355,7 @@ static void write_sigio_workaround(void) ...@@ -336,7 +355,7 @@ static void write_sigio_workaround(void)
close(l_write_sigio_fds[1]); close(l_write_sigio_fds[1]);
} }
void sigio_broken(int fd, int read) void sigio_broken(int fd)
{ {
int err; int err;
...@@ -352,7 +371,7 @@ void sigio_broken(int fd, int read) ...@@ -352,7 +371,7 @@ void sigio_broken(int fd, int read)
all_sigio_fds.poll[all_sigio_fds.used++] = all_sigio_fds.poll[all_sigio_fds.used++] =
((struct pollfd) { .fd = fd, ((struct pollfd) { .fd = fd,
.events = read ? POLLIN : POLLOUT, .events = POLLIN,
.revents = 0 }); .revents = 0 });
out: out:
sigio_unlock(); sigio_unlock();
...@@ -360,17 +379,16 @@ void sigio_broken(int fd, int read) ...@@ -360,17 +379,16 @@ void sigio_broken(int fd, int read)
/* Changed during early boot */ /* Changed during early boot */
static int pty_output_sigio; 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)) if (!isatty(fd))
return; return;
if ((read || pty_output_sigio) && (!read || pty_close_sigio)) if (pty_output_sigio)
return; return;
sigio_broken(fd, read); sigio_broken(fd);
} }
static void sigio_cleanup(void) static void sigio_cleanup(void)
...@@ -514,19 +532,6 @@ static void tty_output(int master, int slave) ...@@ -514,19 +532,6 @@ static void tty_output(int master, int slave)
printk(UM_KERN_CONT "tty_output : read failed, err = %d\n", n); 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) static void __init check_sigio(void)
{ {
if ((access("/dev/ptmx", R_OK) < 0) && if ((access("/dev/ptmx", R_OK) < 0) &&
...@@ -536,7 +541,6 @@ static void __init check_sigio(void) ...@@ -536,7 +541,6 @@ static void __init check_sigio(void)
return; return;
} }
check_one_sigio(tty_output); check_one_sigio(tty_output);
check_one_sigio(tty_close);
} }
/* Here because it only does the SIGIO testing for now */ /* Here because it only does the SIGIO testing for now */
......
...@@ -136,6 +136,16 @@ void set_sigstack(void *sig_stack, int size) ...@@ -136,6 +136,16 @@ void set_sigstack(void *sig_stack, int size)
panic("enabling signal stack failed, errno = %d\n", errno); 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) = { static void (*handlers[_NSIG])(int sig, struct siginfo *si, mcontext_t *mc) = {
[SIGSEGV] = sig_handler, [SIGSEGV] = sig_handler,
[SIGBUS] = sig_handler, [SIGBUS] = sig_handler,
...@@ -145,7 +155,9 @@ static void (*handlers[_NSIG])(int sig, struct siginfo *si, mcontext_t *mc) = { ...@@ -145,7 +155,9 @@ static void (*handlers[_NSIG])(int sig, struct siginfo *si, mcontext_t *mc) = {
[SIGIO] = sig_handler, [SIGIO] = sig_handler,
[SIGWINCH] = 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) static void hard_handler(int sig, siginfo_t *si, void *p)
...@@ -222,6 +234,11 @@ void set_handler(int sig) ...@@ -222,6 +234,11 @@ void set_handler(int sig)
panic("sigprocmask failed - errno = %d\n", errno); panic("sigprocmask failed - errno = %d\n", errno);
} }
void send_sigio_to_self(void)
{
kill(os_getpid(), SIGIO);
}
int change_sig(int signal, int on) int change_sig(int signal, int on)
{ {
sigset_t sigset; sigset_t sigset;
...@@ -254,6 +271,9 @@ void unblock_signals(void) ...@@ -254,6 +271,9 @@ void unblock_signals(void)
return; return;
signals_enabled = 1; 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, * 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) ...@@ -400,7 +400,20 @@ void userspace(struct uml_pt_regs *regs, unsigned long *aux_fp_regs)
if (WIFSTOPPED(status)) { if (WIFSTOPPED(status)) {
int sig = WSTOPSIG(status); int sig = WSTOPSIG(status);
/* 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); ptrace(PTRACE_GETSIGINFO, pid, 0, (struct siginfo *)&si);
break;
}
switch (sig) { switch (sig) {
case SIGSEGV: case SIGSEGV:
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
*/ */
#include <stddef.h> #include <stddef.h>
#include <unistd.h>
#include <errno.h> #include <errno.h>
#include <signal.h> #include <signal.h>
#include <time.h> #include <time.h>
...@@ -99,19 +100,9 @@ long long os_nsecs(void) ...@@ -99,19 +100,9 @@ long long os_nsecs(void)
} }
/** /**
* os_idle_sleep() - sleep for a given time of nsecs * os_idle_sleep() - sleep until interrupted
* @nsecs: nanoseconds to sleep
*/ */
void os_idle_sleep(unsigned long long nsecs) void os_idle_sleep(void)
{ {
struct timespec ts = { pause();
.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();
} }
...@@ -137,20 +137,13 @@ static inline int is_umdir_used(char *dir) ...@@ -137,20 +137,13 @@ static inline int is_umdir_used(char *dir)
{ {
char pid[sizeof("nnnnnnnnn")], *end, *file; char pid[sizeof("nnnnnnnnn")], *end, *file;
int dead, fd, p, n, err; int dead, fd, p, n, err;
size_t filelen; size_t filelen = strlen(dir) + sizeof("/pid") + 1;
err = asprintf(&file, "%s/pid", dir); file = malloc(filelen);
if (err < 0) if (!file)
return 0; return -ENOMEM;
filelen = strlen(file);
n = snprintf(file, filelen, "%s/pid", dir); 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;
}
dead = 0; dead = 0;
fd = open(file, O_RDONLY); fd = open(file, O_RDONLY);
......
...@@ -528,15 +528,15 @@ endif # HW_RANDOM ...@@ -528,15 +528,15 @@ endif # HW_RANDOM
config UML_RANDOM config UML_RANDOM
depends on UML depends on UML
tristate "Hardware random number generator" select HW_RANDOM
tristate "UML Random Number Generator support"
help help
This option enables UML's "hardware" random number generator. It This option enables UML's "hardware" random number generator. It
attaches itself to the host's /dev/random, supplying as much entropy 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 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 own drivers. It registers itself as a rng-core driver thus providing
generator, major 10, minor 183, and the canonical device name is a device which is usually called /dev/hwrng. This hardware random
/dev/hwrng. number generator does feed into the kernel's random number generator
The way to make use of this is to install the rng-tools package entropy pool.
(check your distro, or download from
http://sourceforge.net/projects/gkernel/). rngd periodically reads If unsure, say Y.
/dev/hwrng and injects the entropy into /dev/random.
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