* Re: [PATCH v4 2/5] nohz: support PR_CPU_ISOLATED_STRICT mode
From: Chris Metcalf @ 2015-07-21 19:34 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Gilad Ben Yossef, Steven Rostedt, Ingo Molnar, Peter Zijlstra,
Andrew Morton, Rik van Riel, Tejun Heo, Frederic Weisbecker,
Thomas Gleixner, Paul E. McKenney, Christoph Lameter,
Viresh Kumar, Catalin Marinas, Will Deacon,
linux-doc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Linux API,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <CALCETrUvg+Dix=jG2_1J=mgQC+uRk4dthCYDcb4E5ooEfQjqtQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On 07/13/2015 05:47 PM, Andy Lutomirski wrote:
> On Mon, Jul 13, 2015 at 12:57 PM, Chris Metcalf <cmetcalf-d5a29ZRxExrQT0dZR+AlfA@public.gmane.org> wrote:
>> With cpu_isolated mode, the task is in principle guaranteed not to be
>> interrupted by the kernel, but only if it behaves. In particular, if it
>> enters the kernel via system call, page fault, or any of a number of other
>> synchronous traps, it may be unexpectedly exposed to long latencies.
>> Add a simple flag that puts the process into a state where any such
>> kernel entry is fatal.
>>
> To me, this seems like the wrong design. If nothing else, it seems
> too much like an abusable anti-debugging mechanism. I can imagine
> some per-task flag "I think I shouldn't be interrupted now" and a
> tracepoint that fires if the task is interrupted with that flag set.
> But the strong cpu isolation stuff requires systemwide configuration,
> and I think that monitoring that it works should work similarly.
First, you mention a per-task flag, but not specifically whether the
proposed prctl() mechanism is a reasonable way to set that flag.
Just wanted to clarify that this wasn't an issue in and of itself for you.
Second, you suggest a tracepoint. I'm OK with creating a tracepoint
dedicated to cpu_isolated strict failures and making that the only
way this mechanism works. But, earlier community feedback seemed to
suggest that the signal mechanism was OK; one piece of feedback
just requested being able to set which signal was delivered. Do you
think the signal idea is a bad one? Are you proposing potentially
having a signal and/or a tracepoint?
Last, you mention systemwide configuration for monitoring. Can you
expand on what you mean by that? We already support the monitoring
only on the nohz_full cores, so to that extent it's already systemwide.
And the per-task flag has to be set by the running process when it's
ready for this state, so that can't really be systemwide configuration.
I don't understand your suggestion on this point.
>> diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
>> index d882b833dbdb..7315b1579cbd 100644
>> --- a/arch/arm64/kernel/ptrace.c
>> +++ b/arch/arm64/kernel/ptrace.c
>> @@ -1150,6 +1150,10 @@ static void tracehook_report_syscall(struct pt_regs *regs,
>>
>> asmlinkage int syscall_trace_enter(struct pt_regs *regs)
>> {
>> + /* Ensure we report cpu_isolated violations in all circumstances. */
>> + if (test_thread_flag(TIF_NOHZ) && tick_nohz_cpu_isolated_strict())
>> + tick_nohz_cpu_isolated_syscall(regs->syscallno);
> IMO this is pointless. If a user wants a syscall to kill them, use
> seccomp. The kernel isn't at fault if the user does a syscall when it
> didn't want to enter the kernel.
Interesting! I didn't realize how close SECCOMP_SET_MODE_STRICT
was to what I wanted here. One concern is that there doesn't seem
to be a way to "escape" from seccomp strict mode, i.e. you can't
call seccomp() again to turn it off - which makes sense for seccomp
since it's a security issue, but not so much sense with cpu_isolated.
So, do you think there's a good role for the seccomp() API to play
in achieving this goal? It's certainly not a question of "the kernel at
fault" but rather "asking the kernel to help catch user mistakes"
(typically third-party libraries in our customers' experience). You
could imagine a SECCOMP_SET_MODE_ISOLATED or something.
Alternatively, we could stick with the API proposed in my patch
series, or something similar, and just try to piggy-back on the seccomp
internals to make it happen. It would require Kconfig to ensure
that SECCOMP was enabled though, which obviously isn't currently
required to do cpu isolation.
>> @@ -35,8 +36,12 @@ static inline enum ctx_state exception_enter(void)
>> return 0;
>>
>> prev_ctx = this_cpu_read(context_tracking.state);
>> - if (prev_ctx != CONTEXT_KERNEL)
>> - context_tracking_exit(prev_ctx);
>> + if (prev_ctx != CONTEXT_KERNEL) {
>> + if (context_tracking_exit(prev_ctx)) {
>> + if (tick_nohz_cpu_isolated_strict())
>> + tick_nohz_cpu_isolated_exception();
>> + }
>> + }
> NACK. I'm cautiously optimistic that an x86 kernel 4.3 or newer will
> simply never call exception_enter. It certainly won't call it
> frequently unless something goes wrong with the patches that are
> already in -tip.
This is intended to catch user exceptions like page faults, GPV or
(on platforms where this would happen) unaligned data traps.
The kernel still has a role to play here and cpu_isolated mode
needs to let the user know they have accidentally entered
the kernel in this case.
>> --- a/kernel/context_tracking.c
>> +++ b/kernel/context_tracking.c
>> @@ -147,15 +147,16 @@ NOKPROBE_SYMBOL(context_tracking_user_enter);
>> * This call supports re-entrancy. This way it can be called from any exception
>> * handler without needing to know if we came from userspace or not.
>> */
>> -void context_tracking_exit(enum ctx_state state)
>> +bool context_tracking_exit(enum ctx_state state)
>> {
>> unsigned long flags;
>> + bool from_user = false;
>>
> IMO the internal context tracking API (e.g. context_tracking_exit) are
> mostly of the form "hey context tracking: I don't really know what
> you're doing or what I'm doing, but let me call you and make both of
> us feel better." You're making it somewhat worse: now it's all of the
> above plus "I don't even know whether I just entered the kernel --
> maybe you have a better idea".
>
> Starting with 4.3, x86 kernels will know *exactly* when they enter the
> kernel. All of this context tracking what-was-my-previous-state stuff
> will remain until someone kills it, but when it goes away we'll get a
> nice performance boost.
>
> So, no, let's implement this for real if we're going to implement it.
I'm certainly OK with rebasing on top of 4.3 after the context
tracking stuff is better. That said, I think it makes sense to continue
to debate the intent of the patch series even if we pull this one
patch out and defer it until after 4.3, or having it end up pulled
into some other repo that includes the improvements and
is being pulled for 4.3.
--
Chris Metcalf, EZChip Semiconductor
http://www.ezchip.com
^ permalink raw reply
* Re: [PATCH v4 2/5] nohz: support PR_CPU_ISOLATED_STRICT mode
From: Andy Lutomirski @ 2015-07-21 19:42 UTC (permalink / raw)
To: Chris Metcalf
Cc: Gilad Ben Yossef, Steven Rostedt, Ingo Molnar, Peter Zijlstra,
Andrew Morton, Rik van Riel, Tejun Heo, Frederic Weisbecker,
Thomas Gleixner, Paul E. McKenney, Christoph Lameter,
Viresh Kumar, Catalin Marinas, Will Deacon,
linux-doc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Linux API,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <55AE9EAC.4010202-d5a29ZRxExrQT0dZR+AlfA@public.gmane.org>
On Tue, Jul 21, 2015 at 12:34 PM, Chris Metcalf <cmetcalf-d5a29ZRxExrQT0dZR+AlfA@public.gmane.org> wrote:
> On 07/13/2015 05:47 PM, Andy Lutomirski wrote:
>>
>> On Mon, Jul 13, 2015 at 12:57 PM, Chris Metcalf <cmetcalf-d5a29ZRxExrQT0dZR+AlfA@public.gmane.org>
>> wrote:
>>>
>>> With cpu_isolated mode, the task is in principle guaranteed not to be
>>> interrupted by the kernel, but only if it behaves. In particular, if it
>>> enters the kernel via system call, page fault, or any of a number of
>>> other
>>> synchronous traps, it may be unexpectedly exposed to long latencies.
>>> Add a simple flag that puts the process into a state where any such
>>> kernel entry is fatal.
>>>
>> To me, this seems like the wrong design. If nothing else, it seems
>> too much like an abusable anti-debugging mechanism. I can imagine
>> some per-task flag "I think I shouldn't be interrupted now" and a
>> tracepoint that fires if the task is interrupted with that flag set.
>> But the strong cpu isolation stuff requires systemwide configuration,
>> and I think that monitoring that it works should work similarly.
>
>
> First, you mention a per-task flag, but not specifically whether the
> proposed prctl() mechanism is a reasonable way to set that flag.
> Just wanted to clarify that this wasn't an issue in and of itself for you.
I think I'm okay with a per-task flag for this and, if you add one,
then prctl() is presumably the way to go. Unless people think that
nohz should be 100% reliable always, in which case might as well make
the flag per-cpu.
>
> Second, you suggest a tracepoint. I'm OK with creating a tracepoint
> dedicated to cpu_isolated strict failures and making that the only
> way this mechanism works. But, earlier community feedback seemed to
> suggest that the signal mechanism was OK; one piece of feedback
> just requested being able to set which signal was delivered. Do you
> think the signal idea is a bad one? Are you proposing potentially
> having a signal and/or a tracepoint?
I prefer the tracepoint. It's friendlier to debuggers, and it's
really about diagnosing a kernel problem, not a userspace problem.
Also, I really doubt that people should deploy a signal thing in
production. What if an NMI fires and kills their realtime program?
>
> Last, you mention systemwide configuration for monitoring. Can you
> expand on what you mean by that? We already support the monitoring
> only on the nohz_full cores, so to that extent it's already systemwide.
> And the per-task flag has to be set by the running process when it's
> ready for this state, so that can't really be systemwide configuration.
> I don't understand your suggestion on this point.
I'm really thinking about systemwide configuration for isolation. I
think we'll always (at least in the nearish term) need the admin's
help to set up isolated CPUs. If the admin makes a whole CPU be
isolated, then monitoring just that CPU and monitoring it all the time
seems sensible. If we really do think that isolating a CPU should
require a syscall of some sort because it's too expensive otherwise,
then we can do it that way, too. And if full isolation requires some
user help (e.g. don't do certain things that break isolation), then
having a per-task monitoring flag seems reasonable.
We may always need the user's help to avoid IPIs. For example, if one
thread calls munmap, the other thread is going to get an IPI. There's
nothing we can do about that.
> I'm certainly OK with rebasing on top of 4.3 after the context
> tracking stuff is better. That said, I think it makes sense to continue
> to debate the intent of the patch series even if we pull this one
> patch out and defer it until after 4.3, or having it end up pulled
> into some other repo that includes the improvements and
> is being pulled for 4.3.
Sure, no problem.
--Andy
^ permalink raw reply
* [RFC 1/1 v2] Input: Add ps2emu module
From: Stephen Chandler Paul @ 2015-07-21 19:47 UTC (permalink / raw)
To: Dmitry Torokhov, Andrew Morton, Mauro Carvalho Chehab, Greg KH,
Arnd Bergmann, Joe Perches, Jiri Slaby, Vishnu Patekar,
Sebastian Ott, linux-doc, linux-kernel, linux-input, linux-api
Cc: Benjamin Tissoires, Hans de Goede, Stephen Chandler Paul
In-Reply-To: <20150721191512.GB21710@kroah.com>
Debugging input devices, specifically laptop touchpads, can be tricky
without having the physical device handy. Here we try to remedy that
with ps2emu. This module allows an application to connect to a character
device provided by the kernel, and simulate any PS/2 device. In
combination with userspace programs that can record PS/2 devices and
replay them through the /dev/ps2emu device, this allows developers to
debug driver issues on the PS/2 level with devices simply by requesting
a recording from the user experiencing the issue without having to have
the physical hardware in front of them.
Signed-off-by: Stephen Chandler Paul <cpaul@redhat.com>
Reviewed-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
Changes
* Remove PS2EMU_MINOR, use MISC_DYNAMIC_MINOR
* Remove ps2emu_warn(), just use dev_warn()
* Don't return value from copy_to_user(), return -EFAULT
* Remove usages of unlikely()
* Remove call to nonseekable_open()
Things I didn't change
* Didn't rename this_device, I might have misinterpreted what you were saying
but this_device is a member of a struct that isn't defined in any of my own
patches. I could have renamed ps2emu_misc and ps2emu_fops to misc and fops,
but I'm guessing that's the wrong thing to do if I go off the style of the
other driver files in the kernel tree (in drivers/input, anyway).
Documentation/input/ps2emu.txt | 72 ++++++++++++
MAINTAINERS | 6 +
drivers/input/serio/Kconfig | 10 ++
drivers/input/serio/Makefile | 1 +
drivers/input/serio/ps2emu.c | 250 +++++++++++++++++++++++++++++++++++++++++
include/uapi/linux/ps2emu.h | 42 +++++++
6 files changed, 381 insertions(+)
create mode 100644 Documentation/input/ps2emu.txt
create mode 100644 drivers/input/serio/ps2emu.c
create mode 100644 include/uapi/linux/ps2emu.h
diff --git a/Documentation/input/ps2emu.txt b/Documentation/input/ps2emu.txt
new file mode 100644
index 0000000..560298c
--- /dev/null
+++ b/Documentation/input/ps2emu.txt
@@ -0,0 +1,72 @@
+ The ps2emu Protocol
+ (c) 2015 Stephen Chandler Paul <thatslyude@gmail.com>
+ Sponsored by Red Hat
+--------------------------------------------------------------------------------
+
+1. Introduction
+~~~~~~~~~~~~~~~
+ This module is intended to try to make the lives of input driver developers
+easier by allowing them to test various PS/2 devices (mainly the various
+touchpads found on laptops) without having to have the physical device in front
+of them. ps2emu accomplishes this by allowing any privileged userspace program
+to directly interact with the kernel's serio driver and pretend to be a PS/2
+device.
+
+2. Usage overview
+~~~~~~~~~~~~~~~~~
+ In order to interact with the ps2emu kernel module, one simply opens the
+/dev/ps2emu character device in their applications. Commands are sent to the
+kernel module by writing to the device, and any data received from the serio
+driver is read as-is from the /dev/ps2emu device. All of the structures and
+macros you need to interact with the device are defined in <linux/ps2emu.h>.
+
+3. Command Structure
+~~~~~~~~~~~~~~~~~~~~
+ The struct used for sending commands to /dev/ps2emu is as follows:
+
+ struct ps2emu_cmd {
+ __u8 type;
+ __u8 data;
+ };
+
+ "type" describes the type of command that is being sent. This can be any one
+of the PS2EMU_CMD macros defined in <linux/ps2emu.h>. "data" is the argument
+that goes along with the command. In the event that the command doesn't have an
+argument, this field can be left untouched and will be ignored by the kernel.
+Each command should be sent by writing the struct directly to the character
+device. In the event that the command you send is invalid, an error will be
+returned by the character device and a more descriptive error will be printed
+to the kernel log. Only one command can be sent at a time, any additional data
+written to the character device after the initial command will be ignored.
+ To close the virtual PS/2 port, just close /dev/ps2emu.
+
+4. Commands
+~~~~~~~~~~~
+
+4.1 PS2EMU_CMD_REGISTER
+~~~~~~~~~~~~~~~~~~~~~~~
+ Registers the port with the serio driver and begins transmitting data back and
+forth. Registration can only be performed once a port type is set with
+PS2EMU_CMD_SET_PORT_TYPE. Has no argument.
+
+4.2 PS2EMU_CMD_SET_PORT_TYPE
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ Sets the type of port we're emulating, where "data" is the port type being
+set. Can be any of the following macros from <linux/serio.h>:
+
+ SERIO_8042
+ SERIO_8042_XL
+ SERIO_PS_PSTHRU
+
+4.3 PS2EMU_CMD_SEND_INTERRUPT
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ Sends an interrupt through the virtual PS/2 port to the serio driver, where
+"data" is the interrupt data being sent.
+
+5. Userspace tools
+~~~~~~~~~~~~~~~~~~
+ The ps2emu userspace tools are able to record PS/2 devices using some of the
+debugging information from i8042, and play back the devices on /dev/ps2emu. The
+latest version of these tools can be found at:
+
+ https://github.com/Lyude/ps2emu
diff --git a/MAINTAINERS b/MAINTAINERS
index a226416..68a0977 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10877,6 +10877,12 @@ S: Maintained
F: drivers/media/v4l2-core/videobuf2-*
F: include/media/videobuf2-*
+VIRTUAL PS/2 DEVICE DRIVER
+M: Stephen Chandler Paul <thatslyude@gmail.com>
+S: Maintained
+F: drivers/input/serio/ps2emu.c
+F: include/uapi/linux/ps2emu.h
+
VIRTIO CONSOLE DRIVER
M: Amit Shah <amit.shah@redhat.com>
L: virtualization@lists.linux-foundation.org
diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
index 200841b..cc3563f 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -292,4 +292,14 @@ config SERIO_SUN4I_PS2
To compile this driver as a module, choose M here: the
module will be called sun4i-ps2.
+config PS2EMU
+ tristate "Virtual PS/2 device support"
+ help
+ Say Y here if you want to emulate PS/2 devices using the ps2emu tools.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ps2emu.
+
+ If you are unsure, say N.
+
endif
diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile
index c600089..7b20936 100644
--- a/drivers/input/serio/Makefile
+++ b/drivers/input/serio/Makefile
@@ -30,3 +30,4 @@ obj-$(CONFIG_SERIO_APBPS2) += apbps2.o
obj-$(CONFIG_SERIO_OLPC_APSP) += olpc_apsp.o
obj-$(CONFIG_HYPERV_KEYBOARD) += hyperv-keyboard.o
obj-$(CONFIG_SERIO_SUN4I_PS2) += sun4i-ps2.o
+obj-$(CONFIG_PS2EMU) += ps2emu.o
diff --git a/drivers/input/serio/ps2emu.c b/drivers/input/serio/ps2emu.c
new file mode 100644
index 0000000..73bf389
--- /dev/null
+++ b/drivers/input/serio/ps2emu.c
@@ -0,0 +1,250 @@
+/*
+ * ps2emu kernel PS/2 device emulation module
+ * Copyright (C) 2015 Red Hat
+ * Copyright (C) 2015 Stephen Chandler Paul <thatslyude@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ */
+#include <linux/circ_buf.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <uapi/linux/ps2emu.h>
+
+#define PS2EMU_NAME "ps2emu"
+#define PS2EMU_BUFSIZE 16
+
+static const struct file_operations ps2emu_fops;
+static struct miscdevice ps2emu_misc;
+
+struct ps2emu_device {
+ struct serio serio;
+
+ bool running;
+
+ u8 head;
+ u8 tail;
+ unsigned char buf[PS2EMU_BUFSIZE];
+
+ wait_queue_head_t waitq;
+};
+
+/**
+ * ps2emu_device_write - Write data from serio to a ps2emu device in userspace
+ * @id: The serio port for the ps2emu device
+ * @val: The data to write to the device
+ */
+static int ps2emu_device_write(struct serio *id, unsigned char val)
+{
+ struct ps2emu_device *ps2emu = id->port_data;
+ u8 newhead;
+
+ ps2emu->buf[ps2emu->head] = val;
+
+ newhead = ps2emu->head + 1;
+
+ if (newhead < PS2EMU_BUFSIZE)
+ ps2emu->head = newhead;
+ else
+ ps2emu->head = 0;
+
+ if (newhead == ps2emu->tail)
+ dev_warn(ps2emu_misc.this_device,
+ "Buffer overflowed, ps2emu client isn't keeping up");
+
+ wake_up_interruptible(&ps2emu->waitq);
+
+ return 0;
+}
+
+static int ps2emu_char_open(struct inode *inode, struct file *file)
+{
+ struct ps2emu_device *ps2emu = NULL;
+
+ ps2emu = kzalloc(sizeof(struct ps2emu_device), GFP_KERNEL);
+ if (!ps2emu)
+ return -ENOMEM;
+
+ init_waitqueue_head(&ps2emu->waitq);
+
+ ps2emu->serio.write = ps2emu_device_write;
+ ps2emu->serio.port_data = ps2emu;
+
+ file->private_data = ps2emu;
+
+ return 0;
+}
+
+static int ps2emu_char_release(struct inode *inode, struct file *file)
+{
+ struct ps2emu_device *ps2emu = file->private_data;
+
+ /*
+ * We can rely on serio_unregister_port() to free the ps2emu struct on
+ * it's own
+ */
+ if (ps2emu->running)
+ serio_unregister_port(&ps2emu->serio);
+ else
+ kfree(ps2emu);
+
+ return 0;
+}
+
+static ssize_t ps2emu_char_read(struct file *file, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct ps2emu_device *ps2emu = file->private_data;
+ int ret;
+ size_t nonwrap_len, copylen;
+ u8 head; /* So we only access ps2emu->head once */
+
+ if (file->f_flags & O_NONBLOCK) {
+ head = ps2emu->head;
+
+ if (head == ps2emu->tail)
+ return -EAGAIN;
+ } else {
+ ret = wait_event_interruptible(
+ ps2emu->waitq, (head = ps2emu->head) != ps2emu->tail);
+
+ if (ret)
+ return ret;
+ }
+
+ nonwrap_len = CIRC_CNT_TO_END(head, ps2emu->tail, PS2EMU_BUFSIZE);
+ copylen = min(nonwrap_len, count);
+
+ if (copy_to_user(buffer, &ps2emu->buf[ps2emu->tail], copylen))
+ return -EFAULT;
+
+ ps2emu->tail += copylen;
+ if (ps2emu->tail == PS2EMU_BUFSIZE)
+ ps2emu->tail = 0;
+
+ return copylen;
+}
+
+static ssize_t ps2emu_char_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct ps2emu_device *ps2emu = file->private_data;
+ struct ps2emu_cmd cmd;
+
+ if (count < sizeof(cmd))
+ return -EINVAL;
+
+ if (copy_from_user(&cmd, buffer, sizeof(cmd)))
+ return -EFAULT;
+
+ switch (cmd.type) {
+ case PS2EMU_CMD_REGISTER:
+ if (!ps2emu->serio.id.type) {
+ dev_warn(ps2emu_misc.this_device,
+ "No port type given on /dev/ps2emu\n");
+
+ return -EINVAL;
+ }
+ if (ps2emu->running) {
+ dev_warn(ps2emu_misc.this_device,
+ "Begin command sent, but we're already running\n");
+
+ return -EINVAL;
+ }
+
+ ps2emu->running = true;
+ serio_register_port(&ps2emu->serio);
+ break;
+
+ case PS2EMU_CMD_SET_PORT_TYPE:
+ if (ps2emu->running) {
+ dev_warn(ps2emu_misc.this_device,
+ "Can't change port type on an already running ps2emu instance\n");
+
+ return -EINVAL;
+ }
+
+ switch (cmd.data) {
+ case SERIO_8042:
+ case SERIO_8042_XL:
+ case SERIO_PS_PSTHRU:
+ ps2emu->serio.id.type = cmd.data;
+ break;
+
+ default:
+ dev_warn(ps2emu_misc.this_device,
+ "Invalid port type 0x%hhx\n", cmd.data);
+
+ return -EINVAL;
+ }
+
+ break;
+
+ case PS2EMU_CMD_SEND_INTERRUPT:
+ if (!ps2emu->running) {
+ dev_warn(ps2emu_misc.this_device,
+ "The device must be registered before sending interrupts\n");
+
+ return -EINVAL;
+ }
+
+ serio_interrupt(&ps2emu->serio, cmd.data, 0);
+
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return sizeof(cmd);
+}
+
+static unsigned int ps2emu_char_poll(struct file *file, poll_table *wait)
+{
+ struct ps2emu_device *ps2emu = file->private_data;
+
+ poll_wait(file, &ps2emu->waitq, wait);
+
+ if (ps2emu->head != ps2emu->tail)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+static const struct file_operations ps2emu_fops = {
+ .owner = THIS_MODULE,
+ .open = ps2emu_char_open,
+ .release = ps2emu_char_release,
+ .read = ps2emu_char_read,
+ .write = ps2emu_char_write,
+ .poll = ps2emu_char_poll,
+ .llseek = no_llseek,
+};
+
+static struct miscdevice ps2emu_misc = {
+ .fops = &ps2emu_fops,
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = PS2EMU_NAME,
+};
+
+MODULE_AUTHOR("Stephen Chandler Paul <thatslyude@gmail.com>");
+MODULE_DESCRIPTION("ps2emu");
+MODULE_LICENSE("GPL");
+
+module_driver(ps2emu_misc, misc_register, misc_deregister);
diff --git a/include/uapi/linux/ps2emu.h b/include/uapi/linux/ps2emu.h
new file mode 100644
index 0000000..63f5cc9
--- /dev/null
+++ b/include/uapi/linux/ps2emu.h
@@ -0,0 +1,42 @@
+/*
+ * ps2emu.h
+ * Copyright (C) 2015 Red Hat
+ * Copyright (C) 2015 Lyude (Stephen Chandler Paul) <cpaul@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * This is the public header used for user-space communication with the ps2emu
+ * driver. __attribute__((__packed__)) is used for all structs to keep ABI
+ * compatibility between all architectures.
+ */
+
+#ifndef _PS2EMU_H
+#define _PS2EMU_H
+
+#include <linux/types.h>
+
+#define PS2EMU_CMD_REGISTER 0
+#define PS2EMU_CMD_SET_PORT_TYPE 1
+#define PS2EMU_CMD_SEND_INTERRUPT 2
+
+/*
+ * ps2emu Commands
+ * All commands sent to /dev/ps2emu are encoded using this structure. The type
+ * field should contain a PS2EMU_CMD* value that indicates what kind of command
+ * is being sent to ps2emu. The data field should contain the accompanying
+ * argument for the command, if there is one.
+ */
+struct ps2emu_cmd {
+ __u8 type;
+ __u8 data;
+} __attribute__((__packed__));
+
+#endif /* !_PS2EMU_H */
--
2.4.3
^ permalink raw reply related
* Re: [RFC 1/1 v2] Input: Add ps2emu module
From: Greg KH @ 2015-07-21 19:57 UTC (permalink / raw)
To: Stephen Chandler Paul
Cc: Dmitry Torokhov, Andrew Morton, Mauro Carvalho Chehab,
Arnd Bergmann, Joe Perches, Jiri Slaby, Vishnu Patekar,
Sebastian Ott, linux-doc, linux-kernel, linux-input, linux-api,
Benjamin Tissoires, Hans de Goede
In-Reply-To: <1437508037-13928-1-git-send-email-cpaul@redhat.com>
On Tue, Jul 21, 2015 at 03:47:17PM -0400, Stephen Chandler Paul wrote:
> Debugging input devices, specifically laptop touchpads, can be tricky
> without having the physical device handy. Here we try to remedy that
> with ps2emu. This module allows an application to connect to a character
> device provided by the kernel, and simulate any PS/2 device. In
> combination with userspace programs that can record PS/2 devices and
> replay them through the /dev/ps2emu device, this allows developers to
> debug driver issues on the PS/2 level with devices simply by requesting
> a recording from the user experiencing the issue without having to have
> the physical hardware in front of them.
>
> Signed-off-by: Stephen Chandler Paul <cpaul@redhat.com>
> Reviewed-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> ---
> Changes
> * Remove PS2EMU_MINOR, use MISC_DYNAMIC_MINOR
> * Remove ps2emu_warn(), just use dev_warn()
> * Don't return value from copy_to_user(), return -EFAULT
> * Remove usages of unlikely()
> * Remove call to nonseekable_open()
>
> Things I didn't change
> * Didn't rename this_device, I might have misinterpreted what you were saying
> but this_device is a member of a struct that isn't defined in any of my own
> patches. I could have renamed ps2emu_misc and ps2emu_fops to misc and fops,
> but I'm guessing that's the wrong thing to do if I go off the style of the
> other driver files in the kernel tree (in drivers/input, anyway).
Sorry, you are right, this is a structure that you don't have control
over. Thanks for the other changes, looks good to me.
greg k-h
^ permalink raw reply
* [PATCH V4 0/6] Allow user to request memory to be locked on page fault
From: Eric B Munson @ 2015-07-21 19:59 UTC (permalink / raw)
To: Andrew Morton
Cc: Eric B Munson, Shuah Khan, Michal Hocko, Michael Kerrisk,
Vlastimil Babka, Jonathan Corbet, linux-alpha, linux-kernel,
linux-mips, linux-parisc, linuxppc-dev, sparclinux, linux-xtensa,
linux-mm, linux-arch, linux-api
mlock() allows a user to control page out of program memory, but this
comes at the cost of faulting in the entire mapping when it is
allocated. For large mappings where the entire area is not necessary
this is not ideal. Instead of forcing all locked pages to be present
when they are allocated, this set creates a middle ground. Pages are
marked to be placed on the unevictable LRU (locked) when they are first
used, but they are not faulted in by the mlock call.
This series introduces a new mlock() system call that takes a flags
argument along with the start address and size. This flags argument
gives the caller the ability to request memory be locked in the
traditional way, or to be locked after the page is faulted in. New
calls are added for munlock() and munlockall() which give the called a
way to specify which flags are supposed to be cleared. A new MCL flag
is added to mirror the lock on fault behavior from mlock() in
mlockall(). Finally, a flag for mmap() is added that allows a user to
specify that the covered are should not be paged out, but only after the
memory has been used the first time.
There are two main use cases that this set covers. The first is the
security focussed mlock case. A buffer is needed that cannot be written
to swap. The maximum size is known, but on average the memory used is
significantly less than this maximum. With lock on fault, the buffer
is guaranteed to never be paged out without consuming the maximum size
every time such a buffer is created.
The second use case is focussed on performance. Portions of a large
file are needed and we want to keep the used portions in memory once
accessed. This is the case for large graphical models where the path
through the graph is not known until run time. The entire graph is
unlikely to be used in a given invocation, but once a node has been
used it needs to stay resident for further processing. Given these
constraints we have a number of options. We can potentially waste a
large amount of memory by mlocking the entire region (this can also
cause a significant stall at startup as the entire file is read in).
We can mlock every page as we access them without tracking if the page
is already resident but this introduces large overhead for each access.
The third option is mapping the entire region with PROT_NONE and using
a signal handler for SIGSEGV to mprotect(PROT_READ) and mlock() the
needed page. Doing this page at a time adds a significant performance
penalty. Batching can be used to mitigate this overhead, but in order
to safely avoid trying to mprotect pages outside of the mapping, the
boundaries of each mapping to be used in this way must be tracked and
available to the signal handler. This is precisely what the mm system
in the kernel should already be doing.
For mlock(MLOCK_ONFAULT) and mmap(MAP_LOCKONFAULT) the user is charged
against RLIMIT_MEMLOCK as if mlock(MLOCK_LOCKED) or mmap(MAP_LOCKED) was
used, so when the VMA is created not when the pages are faulted in. For
mlockall(MCL_ONFAULT) the user is charged as if MCL_FUTURE was used.
This decision was made to keep the accounting checks out of the page
fault path.
To illustrate the benefit of this set I wrote a test program that mmaps
a 5 GB file filled with random data and then makes 15,000,000 accesses
to random addresses in that mapping. The test program was run 20 times
for each setup. Results are reported for two program portions, setup
and execution. The setup phase is calling mmap and optionally mlock on
the entire region. For most experiments this is trivial, but it
highlights the cost of faulting in the entire region. Results are
averages across the 20 runs in milliseconds.
mmap with mlock(MLOCK_LOCKED) on entire range:
Setup avg: 8228.666
Processing avg: 8274.257
mmap with mlock(MLOCK_LOCKED) before each access:
Setup avg: 0.113
Processing avg: 90993.552
mmap with PROT_NONE and signal handler and batch size of 1 page:
With the default value in max_map_count, this gets ENOMEM as I attempt
to change the permissions, after upping the sysctl significantly I get:
Setup avg: 0.058
Processing avg: 69488.073
mmap with PROT_NONE and signal handler and batch size of 8 pages:
Setup avg: 0.068
Processing avg: 38204.116
mmap with PROT_NONE and signal handler and batch size of 16 pages:
Setup avg: 0.044
Processing avg: 29671.180
mmap with mlock(MLOCK_ONFAULT) on entire range:
Setup avg: 0.189
Processing avg: 17904.899
The signal handler in the batch cases faulted in memory in two steps to
avoid having to know the start and end of the faulting mapping. The
first step covers the page that caused the fault as we know that it will
be possible to lock. The second step speculatively tries to mlock and
mprotect the batch size - 1 pages that follow. There may be a clever
way to avoid this without having the program track each mapping to be
covered by this handeler in a globally accessible structure, but I could
not find it. It should be noted that with a large enough batch size
this two step fault handler can still cause the program to crash if it
reaches far beyond the end of the mapping.
These results show that if the developer knows that a majority of the
mapping will be used, it is better to try and fault it in at once,
otherwise MAP_LOCKONFAULT is significantly faster.
The performance cost of these patches are minimal on the two benchmarks
I have tested (stream and kernbench). The following are the average
values across 20 runs of stream and 10 runs of kernbench after a warmup
run whose results were discarded.
Avg throughput in MB/s from stream using 1000000 element arrays
Test 4.2-rc1 4.2-rc1+lock-on-fault
Copy: 10,566.5 10,421
Scale: 10,685 10,503.5
Add: 12,044.1 11,814.2
Triad: 12,064.8 11,846.3
Kernbench optimal load
4.2-rc1 4.2-rc1+lock-on-fault
Elapsed Time 78.453 78.991
User Time 64.2395 65.2355
System Time 9.7335 9.7085
Context Switches 22211.5 22412.1
Sleeps 14965.3 14956.1
---
Changes from V3:
Ensure that pages present when mlock2(MLOCK_ONFAULT) is called are locked
Ensure that VM_LOCKONFAULT is handled in cases that used to only check VM_LOCKED
Add tests for new system calls
Add missing syscall entries, fix NR_syscalls on multiple arch's
Add missing MAP_LOCKONFAULT for tile
Changes from V2:
Added new system calls for mlock, munlock, and munlockall with added
flags arguments for controlling how memory is locked or unlocked.
Eric B Munson (6):
mm: mlock: Refactor mlock, munlock, and munlockall code
mm: mlock: Add new mlock, munlock, and munlockall system calls
mm: gup: Add mm_lock_present()
mm: mlock: Introduce VM_LOCKONFAULT and add mlock flags to enable it
mm: mmap: Add mmap flag to request VM_LOCKONFAULT
selftests: vm: Add tests for lock on fault
arch/alpha/include/asm/unistd.h | 2 +-
arch/alpha/include/uapi/asm/mman.h | 5 +
arch/alpha/include/uapi/asm/unistd.h | 3 +
arch/alpha/kernel/systbls.S | 3 +
arch/arm/include/asm/unistd.h | 2 +-
arch/arm/include/uapi/asm/unistd.h | 3 +
arch/arm/kernel/calls.S | 3 +
arch/arm64/include/asm/unistd32.h | 6 +
arch/avr32/include/uapi/asm/unistd.h | 3 +
arch/avr32/kernel/syscall_table.S | 3 +
arch/blackfin/include/uapi/asm/unistd.h | 3 +
arch/blackfin/mach-common/entry.S | 3 +
arch/cris/arch-v10/kernel/entry.S | 3 +
arch/cris/arch-v32/kernel/entry.S | 3 +
arch/frv/kernel/entry.S | 3 +
arch/ia64/include/asm/unistd.h | 2 +-
arch/ia64/include/uapi/asm/unistd.h | 3 +
arch/ia64/kernel/entry.S | 3 +
arch/m32r/kernel/entry.S | 3 +
arch/m32r/kernel/syscall_table.S | 3 +
arch/m68k/include/asm/unistd.h | 2 +-
arch/m68k/include/uapi/asm/unistd.h | 3 +
arch/m68k/kernel/syscalltable.S | 3 +
arch/microblaze/include/uapi/asm/unistd.h | 3 +
arch/microblaze/kernel/syscall_table.S | 3 +
arch/mips/include/uapi/asm/mman.h | 8 +
arch/mips/include/uapi/asm/unistd.h | 21 +-
arch/mips/kernel/scall32-o32.S | 3 +
arch/mips/kernel/scall64-64.S | 3 +
arch/mips/kernel/scall64-n32.S | 3 +
arch/mips/kernel/scall64-o32.S | 3 +
arch/mn10300/kernel/entry.S | 3 +
arch/parisc/include/uapi/asm/mman.h | 5 +
arch/parisc/include/uapi/asm/unistd.h | 5 +-
arch/powerpc/include/uapi/asm/mman.h | 5 +
arch/powerpc/include/uapi/asm/unistd.h | 3 +
arch/s390/include/uapi/asm/unistd.h | 5 +-
arch/s390/kernel/compat_wrapper.c | 3 +
arch/s390/kernel/syscalls.S | 3 +
arch/sh/kernel/syscalls_32.S | 3 +
arch/sparc/include/uapi/asm/mman.h | 5 +
arch/sparc/include/uapi/asm/unistd.h | 5 +-
arch/sparc/kernel/systbls_32.S | 2 +-
arch/sparc/kernel/systbls_64.S | 4 +-
arch/tile/include/uapi/asm/mman.h | 9 +
arch/x86/entry/syscalls/syscall_32.tbl | 3 +
arch/x86/entry/syscalls/syscall_64.tbl | 3 +
arch/xtensa/include/uapi/asm/mman.h | 8 +
arch/xtensa/include/uapi/asm/unistd.h | 10 +-
drivers/gpu/drm/drm_vm.c | 8 +-
fs/proc/task_mmu.c | 3 +-
include/linux/mm.h | 2 +
include/linux/mman.h | 3 +-
include/linux/syscalls.h | 4 +
include/uapi/asm-generic/mman.h | 5 +
include/uapi/asm-generic/unistd.h | 8 +-
kernel/events/core.c | 2 +
kernel/events/uprobes.c | 2 +-
kernel/fork.c | 2 +-
kernel/sys_ni.c | 3 +
mm/debug.c | 1 +
mm/gup.c | 175 +++++++-
mm/huge_memory.c | 3 +-
mm/hugetlb.c | 4 +-
mm/internal.h | 5 +-
mm/ksm.c | 2 +-
mm/madvise.c | 4 +-
mm/memory.c | 5 +-
mm/mlock.c | 159 +++++--
mm/mmap.c | 32 +-
mm/mremap.c | 6 +-
mm/msync.c | 2 +-
mm/rmap.c | 12 +-
mm/shmem.c | 2 +-
mm/swap.c | 3 +-
mm/vmscan.c | 2 +-
tools/testing/selftests/vm/Makefile | 3 +
tools/testing/selftests/vm/lock-on-fault.c | 344 +++++++++++++++
tools/testing/selftests/vm/mlock2-tests.c | 621 ++++++++++++++++++++++++++++
tools/testing/selftests/vm/on-fault-limit.c | 47 +++
tools/testing/selftests/vm/run_vmtests | 33 ++
81 files changed, 1604 insertions(+), 104 deletions(-)
create mode 100644 tools/testing/selftests/vm/lock-on-fault.c
create mode 100644 tools/testing/selftests/vm/mlock2-tests.c
create mode 100644 tools/testing/selftests/vm/on-fault-limit.c
Cc: Shuah Khan <shuahkh@osg.samsung.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Michael Kerrisk <mtk.manpages@gmail.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: linux-alpha@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-mips@linux-mips.org
Cc: linux-parisc@vger.kernel.org
Cc: linuxppc-dev@lists.ozlabs.org
Cc: sparclinux@vger.kernel.org
Cc: linux-xtensa@linux-xtensa.org
Cc: linux-mm@kvack.org
Cc: linux-arch@vger.kernel.org
Cc: linux-api@vger.kernel.org
--
1.9.1
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* [PATCH V4 2/6] mm: mlock: Add new mlock, munlock, and munlockall system calls
From: Eric B Munson @ 2015-07-21 19:59 UTC (permalink / raw)
To: Andrew Morton
Cc: Eric B Munson, Michal Hocko, Vlastimil Babka, Heiko Carstens,
Geert Uytterhoeven, Catalin Marinas, Stephen Rothwell,
Guenter Roeck, linux-alpha, linux-kernel, linux-arm-kernel,
adi-buildroot-devel, linux-cris-kernel, linux-ia64, linux-m68k,
linux-mips, linux-am33-list, linux-parisc, linuxppc-dev,
linux-s390, linux-sh, sparclinux, linux-xtensa, linux-api,
linux-arch, linux-mm
In-Reply-To: <1437508781-28655-1-git-send-email-emunson@akamai.com>
With the refactored mlock code, introduce new system calls for mlock,
munlock, and munlockall. The new calls will allow the user to specify
what lock states are being added or cleared. mlock2 and munlock2 are
trivial at the moment, but a follow on patch will add a new mlock state
making them useful.
munlock2 addresses a limitation of the current implementation. If a
user calls mlockall(MCL_CURRENT | MCL_FUTURE) and then later decides
that MCL_FUTURE should be removed, they would have to call munlockall()
followed by mlockall(MCL_CURRENT) which could potentially be very
expensive. The new munlockall2 system call allows a user to simply
clear the MCL_FUTURE flag.
Signed-off-by: Eric B Munson <emunson@akamai.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
Cc: Geert Uytterhoeven <geert@linux-m68k.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Stephen Rothwell <sfr@canb.auug.org.au>
Cc: Guenter Roeck <linux@roeck-us.net>
Cc: linux-alpha@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: adi-buildroot-devel@lists.sourceforge.net
Cc: linux-cris-kernel@axis.com
Cc: linux-ia64@vger.kernel.org
Cc: linux-m68k@lists.linux-m68k.org
Cc: linux-mips@linux-mips.org
Cc: linux-am33-list@redhat.com
Cc: linux-parisc@vger.kernel.org
Cc: linuxppc-dev@lists.ozlabs.org
Cc: linux-s390@vger.kernel.org
Cc: linux-sh@vger.kernel.org
Cc: sparclinux@vger.kernel.org
Cc: linux-xtensa@linux-xtensa.org
Cc: linux-api@vger.kernel.org
Cc: linux-arch@vger.kernel.org
Cc: linux-mm@kvack.org
---
Changes from V3:
* Do a (hopefully) complete job of adding the new system calls
arch/alpha/include/asm/unistd.h | 2 +-
arch/alpha/include/uapi/asm/mman.h | 2 ++
arch/alpha/include/uapi/asm/unistd.h | 3 +++
arch/alpha/kernel/systbls.S | 3 +++
arch/arm/include/asm/unistd.h | 2 +-
arch/arm/include/uapi/asm/unistd.h | 3 +++
arch/arm/kernel/calls.S | 3 +++
arch/arm64/include/asm/unistd32.h | 6 ++++++
arch/avr32/include/uapi/asm/unistd.h | 3 +++
arch/avr32/kernel/syscall_table.S | 3 +++
arch/blackfin/include/uapi/asm/unistd.h | 3 +++
arch/blackfin/mach-common/entry.S | 3 +++
arch/cris/arch-v10/kernel/entry.S | 3 +++
arch/cris/arch-v32/kernel/entry.S | 3 +++
arch/frv/kernel/entry.S | 3 +++
arch/ia64/include/asm/unistd.h | 2 +-
arch/ia64/include/uapi/asm/unistd.h | 3 +++
arch/ia64/kernel/entry.S | 3 +++
arch/m32r/kernel/entry.S | 3 +++
arch/m32r/kernel/syscall_table.S | 3 +++
arch/m68k/include/asm/unistd.h | 2 +-
arch/m68k/include/uapi/asm/unistd.h | 3 +++
arch/m68k/kernel/syscalltable.S | 3 +++
arch/microblaze/include/uapi/asm/unistd.h | 3 +++
arch/microblaze/kernel/syscall_table.S | 3 +++
arch/mips/include/uapi/asm/mman.h | 5 +++++
arch/mips/include/uapi/asm/unistd.h | 21 +++++++++++++++------
arch/mips/kernel/scall32-o32.S | 3 +++
arch/mips/kernel/scall64-64.S | 3 +++
arch/mips/kernel/scall64-n32.S | 3 +++
arch/mips/kernel/scall64-o32.S | 3 +++
arch/mn10300/kernel/entry.S | 3 +++
arch/parisc/include/uapi/asm/mman.h | 2 ++
arch/parisc/include/uapi/asm/unistd.h | 5 ++++-
arch/powerpc/include/uapi/asm/mman.h | 2 ++
arch/powerpc/include/uapi/asm/unistd.h | 3 +++
arch/s390/include/uapi/asm/unistd.h | 5 ++++-
arch/s390/kernel/compat_wrapper.c | 3 +++
arch/s390/kernel/syscalls.S | 3 +++
arch/sh/kernel/syscalls_32.S | 3 +++
arch/sparc/include/uapi/asm/mman.h | 2 ++
arch/sparc/include/uapi/asm/unistd.h | 5 ++++-
arch/sparc/kernel/systbls_32.S | 2 +-
arch/sparc/kernel/systbls_64.S | 4 ++--
arch/tile/include/uapi/asm/mman.h | 5 +++++
arch/x86/entry/syscalls/syscall_32.tbl | 3 +++
arch/x86/entry/syscalls/syscall_64.tbl | 3 +++
arch/xtensa/include/uapi/asm/mman.h | 5 +++++
arch/xtensa/include/uapi/asm/unistd.h | 10 ++++++++--
include/linux/syscalls.h | 4 ++++
include/uapi/asm-generic/mman.h | 2 ++
include/uapi/asm-generic/unistd.h | 8 +++++++-
kernel/sys_ni.c | 3 +++
mm/mlock.c | 28 ++++++++++++++++++++++++++++
54 files changed, 205 insertions(+), 19 deletions(-)
diff --git a/arch/alpha/include/asm/unistd.h b/arch/alpha/include/asm/unistd.h
index a56e608..1d09392 100644
--- a/arch/alpha/include/asm/unistd.h
+++ b/arch/alpha/include/asm/unistd.h
@@ -3,7 +3,7 @@
#include <uapi/asm/unistd.h>
-#define NR_SYSCALLS 514
+#define NR_SYSCALLS 517
#define __ARCH_WANT_OLD_READDIR
#define __ARCH_WANT_STAT64
diff --git a/arch/alpha/include/uapi/asm/mman.h b/arch/alpha/include/uapi/asm/mman.h
index 0086b47..ec72436 100644
--- a/arch/alpha/include/uapi/asm/mman.h
+++ b/arch/alpha/include/uapi/asm/mman.h
@@ -38,6 +38,8 @@
#define MCL_CURRENT 8192 /* lock all currently mapped pages */
#define MCL_FUTURE 16384 /* lock all additions to address space */
+#define MLOCK_LOCKED 0x01 /* Lock and populate the specified range */
+
#define MADV_NORMAL 0 /* no further special treatment */
#define MADV_RANDOM 1 /* expect random page references */
#define MADV_SEQUENTIAL 2 /* expect sequential page references */
diff --git a/arch/alpha/include/uapi/asm/unistd.h b/arch/alpha/include/uapi/asm/unistd.h
index aa33bf5..29141d6 100644
--- a/arch/alpha/include/uapi/asm/unistd.h
+++ b/arch/alpha/include/uapi/asm/unistd.h
@@ -475,5 +475,8 @@
#define __NR_getrandom 511
#define __NR_memfd_create 512
#define __NR_execveat 513
+#define __NR_mlock2 514
+#define __NR_munlock2 515
+#define __NR_munlockall2 516
#endif /* _UAPI_ALPHA_UNISTD_H */
diff --git a/arch/alpha/kernel/systbls.S b/arch/alpha/kernel/systbls.S
index 9b62e3f..04d1cce 100644
--- a/arch/alpha/kernel/systbls.S
+++ b/arch/alpha/kernel/systbls.S
@@ -532,6 +532,9 @@ sys_call_table:
.quad sys_getrandom
.quad sys_memfd_create
.quad sys_execveat
+ .quad sys_mlock2
+ .quad sys_munlock2 /* 515 */
+ .quad sys_munlockall2
.size sys_call_table, . - sys_call_table
.type sys_call_table, @object
diff --git a/arch/arm/include/asm/unistd.h b/arch/arm/include/asm/unistd.h
index 32640c4..7cba573 100644
--- a/arch/arm/include/asm/unistd.h
+++ b/arch/arm/include/asm/unistd.h
@@ -19,7 +19,7 @@
* This may need to be greater than __NR_last_syscall+1 in order to
* account for the padding in the syscall table
*/
-#define __NR_syscalls (388)
+#define __NR_syscalls (392)
/*
* *NOTE*: This is a ghost syscall private to the kernel. Only the
diff --git a/arch/arm/include/uapi/asm/unistd.h b/arch/arm/include/uapi/asm/unistd.h
index 0c3f5a0..46eaf405 100644
--- a/arch/arm/include/uapi/asm/unistd.h
+++ b/arch/arm/include/uapi/asm/unistd.h
@@ -414,6 +414,9 @@
#define __NR_memfd_create (__NR_SYSCALL_BASE+385)
#define __NR_bpf (__NR_SYSCALL_BASE+386)
#define __NR_execveat (__NR_SYSCALL_BASE+387)
+#define __NR_mlock2 (__NR_SYSCALL_BASE+388)
+#define __NR_munlock2 (__NR_SYSCALL_BASE+389)
+#define __NR_munlockall2 (__NR_SYSCALL_BASE+390)
/*
* The following SWIs are ARM private.
diff --git a/arch/arm/kernel/calls.S b/arch/arm/kernel/calls.S
index 05745eb..8880822 100644
--- a/arch/arm/kernel/calls.S
+++ b/arch/arm/kernel/calls.S
@@ -397,6 +397,9 @@
/* 385 */ CALL(sys_memfd_create)
CALL(sys_bpf)
CALL(sys_execveat)
+ CALL(sys_mlock2)
+ CALL(sys_munlock2)
+/* 390 */ CALL(sys_munlockall2)
#ifndef syscalls_counted
.equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls
#define syscalls_counted
diff --git a/arch/arm64/include/asm/unistd32.h b/arch/arm64/include/asm/unistd32.h
index cef934a..318072aa 100644
--- a/arch/arm64/include/asm/unistd32.h
+++ b/arch/arm64/include/asm/unistd32.h
@@ -797,3 +797,9 @@ __SYSCALL(__NR_memfd_create, sys_memfd_create)
__SYSCALL(__NR_bpf, sys_bpf)
#define __NR_execveat 387
__SYSCALL(__NR_execveat, compat_sys_execveat)
+#define __NR_mlock2 388
+__SYSCALL(__NR_mlock2, sys_mlock2)
+#define __NR_munlock2 389
+__SYSCALL(__NR_munlock2, sys_munlock2)
+#define __NR_munlockall2 390
+__SYSCALL(__NR_munlockall2, sys_munlockall2)
diff --git a/arch/avr32/include/uapi/asm/unistd.h b/arch/avr32/include/uapi/asm/unistd.h
index bbe2fba..e6a1681 100644
--- a/arch/avr32/include/uapi/asm/unistd.h
+++ b/arch/avr32/include/uapi/asm/unistd.h
@@ -333,5 +333,8 @@
#define __NR_memfd_create 318
#define __NR_bpf 319
#define __NR_execveat 320
+#define __NR_mlock2 321
+#define __NR_munlock2 322
+#define __NR_munlockall2 323
#endif /* _UAPI__ASM_AVR32_UNISTD_H */
diff --git a/arch/avr32/kernel/syscall_table.S b/arch/avr32/kernel/syscall_table.S
index c3b593b..83928ab 100644
--- a/arch/avr32/kernel/syscall_table.S
+++ b/arch/avr32/kernel/syscall_table.S
@@ -334,4 +334,7 @@ sys_call_table:
.long sys_memfd_create
.long sys_bpf
.long sys_execveat /* 320 */
+ .long sys_mlock2
+ .long sys_munlock2
+ .long sys_munlockall2
.long sys_ni_syscall /* r8 is saturated at nr_syscalls */
diff --git a/arch/blackfin/include/uapi/asm/unistd.h b/arch/blackfin/include/uapi/asm/unistd.h
index 0cb9078..37c0362 100644
--- a/arch/blackfin/include/uapi/asm/unistd.h
+++ b/arch/blackfin/include/uapi/asm/unistd.h
@@ -433,6 +433,9 @@
#define __IGNORE_munlock
#define __IGNORE_mlockall
#define __IGNORE_munlockall
+#define __IGNORE_mlock2
+#define __IGNORE_munlock2
+#define __IGNORE_munlockall2
#define __IGNORE_mincore
#define __IGNORE_madvise
#define __IGNORE_remap_file_pages
diff --git a/arch/blackfin/mach-common/entry.S b/arch/blackfin/mach-common/entry.S
index 8d9431e..5d83587 100644
--- a/arch/blackfin/mach-common/entry.S
+++ b/arch/blackfin/mach-common/entry.S
@@ -1704,6 +1704,9 @@ ENTRY(_sys_call_table)
.long _sys_memfd_create /* 390 */
.long _sys_bpf
.long _sys_execveat
+ .long _sys_mlock2
+ .long _sys_munlock2
+ .long _sys_munlockall2 /* 395 */
.rept NR_syscalls-(.-_sys_call_table)/4
.long _sys_ni_syscall
diff --git a/arch/cris/arch-v10/kernel/entry.S b/arch/cris/arch-v10/kernel/entry.S
index 81570fc..d0ce531 100644
--- a/arch/cris/arch-v10/kernel/entry.S
+++ b/arch/cris/arch-v10/kernel/entry.S
@@ -955,6 +955,9 @@ sys_call_table:
.long sys_process_vm_writev
.long sys_kcmp /* 350 */
.long sys_finit_module
+ .long sys_mlock2
+ .long sys_munlock2
+ .long sys_munlockall2
/*
* NOTE!! This doesn't have to be exact - we just have
diff --git a/arch/cris/arch-v32/kernel/entry.S b/arch/cris/arch-v32/kernel/entry.S
index 026a0b2..7f50a0b 100644
--- a/arch/cris/arch-v32/kernel/entry.S
+++ b/arch/cris/arch-v32/kernel/entry.S
@@ -875,6 +875,9 @@ sys_call_table:
.long sys_process_vm_writev
.long sys_kcmp /* 350 */
.long sys_finit_module
+ .long sys_mlock2
+ .long sys_munlock2
+ .long sys_munlockall2
/*
* NOTE!! This doesn't have to be exact - we just have
diff --git a/arch/frv/kernel/entry.S b/arch/frv/kernel/entry.S
index dfcd263..ee605a0 100644
--- a/arch/frv/kernel/entry.S
+++ b/arch/frv/kernel/entry.S
@@ -1515,5 +1515,8 @@ sys_call_table:
.long sys_rt_tgsigqueueinfo /* 335 */
.long sys_perf_event_open
.long sys_setns
+ .long sys_mlock2
+ .long sys_munlock2
+ .long sys_munlockall2 /* 340 */
syscall_table_size = (. - sys_call_table)
diff --git a/arch/ia64/include/asm/unistd.h b/arch/ia64/include/asm/unistd.h
index 95c39b9..db73390 100644
--- a/arch/ia64/include/asm/unistd.h
+++ b/arch/ia64/include/asm/unistd.h
@@ -11,7 +11,7 @@
-#define NR_syscalls 319 /* length of syscall table */
+#define NR_syscalls 322 /* length of syscall table */
/*
* The following defines stop scripts/checksyscalls.sh from complaining about
diff --git a/arch/ia64/include/uapi/asm/unistd.h b/arch/ia64/include/uapi/asm/unistd.h
index 4610795..5f485cc 100644
--- a/arch/ia64/include/uapi/asm/unistd.h
+++ b/arch/ia64/include/uapi/asm/unistd.h
@@ -332,5 +332,8 @@
#define __NR_memfd_create 1340
#define __NR_bpf 1341
#define __NR_execveat 1342
+#define __NR_mlock2 1343
+#define __NR_munlock2 1344
+#define __NR_munlockall2 1345
#endif /* _UAPI_ASM_IA64_UNISTD_H */
diff --git a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S
index ae0de7b..3ef4457 100644
--- a/arch/ia64/kernel/entry.S
+++ b/arch/ia64/kernel/entry.S
@@ -1768,5 +1768,8 @@ sys_call_table:
data8 sys_memfd_create // 1340
data8 sys_bpf
data8 sys_execveat
+ data8 sys_mlock2
+ data8 sys_munlock2
+ data8 sys_munlockall2 // 1345
.org sys_call_table + 8*NR_syscalls // guard against failures to increase NR_syscalls
diff --git a/arch/m32r/kernel/entry.S b/arch/m32r/kernel/entry.S
index c639bfa..4f7f2e2 100644
--- a/arch/m32r/kernel/entry.S
+++ b/arch/m32r/kernel/entry.S
@@ -76,6 +76,9 @@
#define sys_munlock sys_ni_syscall
#define sys_mlockall sys_ni_syscall
#define sys_munlockall sys_ni_syscall
+#define sys_mlock2 sys_ni_syscall
+#define sys_munlock2 sys_ni_syscall
+#define sys_munlockall2 sys_ni_syscall
#define sys_mremap sys_ni_syscall
#define sys_mincore sys_ni_syscall
#define sys_remap_file_pages sys_ni_syscall
diff --git a/arch/m32r/kernel/syscall_table.S b/arch/m32r/kernel/syscall_table.S
index f365c19..9918c3e 100644
--- a/arch/m32r/kernel/syscall_table.S
+++ b/arch/m32r/kernel/syscall_table.S
@@ -325,3 +325,6 @@ ENTRY(sys_call_table)
.long sys_eventfd
.long sys_fallocate
.long sys_setns /* 325 */
+ .long sys_mlock2
+ .long sys_munlock2
+ .long sys_munlockall2
diff --git a/arch/m68k/include/asm/unistd.h b/arch/m68k/include/asm/unistd.h
index 244e0db..b18f3da 100644
--- a/arch/m68k/include/asm/unistd.h
+++ b/arch/m68k/include/asm/unistd.h
@@ -4,7 +4,7 @@
#include <uapi/asm/unistd.h>
-#define NR_syscalls 356
+#define NR_syscalls 359
#define __ARCH_WANT_OLD_READDIR
#define __ARCH_WANT_OLD_STAT
diff --git a/arch/m68k/include/uapi/asm/unistd.h b/arch/m68k/include/uapi/asm/unistd.h
index 61fb6cb..1405c3f 100644
--- a/arch/m68k/include/uapi/asm/unistd.h
+++ b/arch/m68k/include/uapi/asm/unistd.h
@@ -361,5 +361,8 @@
#define __NR_memfd_create 353
#define __NR_bpf 354
#define __NR_execveat 355
+#define __NR_mlock2 356
+#define __NR_munlock2 357
+#define __NR_munlockall2 358
#endif /* _UAPI_ASM_M68K_UNISTD_H_ */
diff --git a/arch/m68k/kernel/syscalltable.S b/arch/m68k/kernel/syscalltable.S
index a0ec430..7963c03 100644
--- a/arch/m68k/kernel/syscalltable.S
+++ b/arch/m68k/kernel/syscalltable.S
@@ -376,4 +376,7 @@ ENTRY(sys_call_table)
.long sys_memfd_create
.long sys_bpf
.long sys_execveat /* 355 */
+ .long sys_mlock2
+ .long sys_munlock2
+ .long sys_munlockall2
diff --git a/arch/microblaze/include/uapi/asm/unistd.h b/arch/microblaze/include/uapi/asm/unistd.h
index 32850c7..59b06b0 100644
--- a/arch/microblaze/include/uapi/asm/unistd.h
+++ b/arch/microblaze/include/uapi/asm/unistd.h
@@ -404,5 +404,8 @@
#define __NR_memfd_create 386
#define __NR_bpf 387
#define __NR_execveat 388
+#define __NR_mlock2 389 /* ok - nommu or mmu */
+#define __NR_munlock2 390 /* ok - nommu or mmu */
+#define __NR_munlockall2 391 /* ok - nommu or mmu */
#endif /* _UAPI_ASM_MICROBLAZE_UNISTD_H */
diff --git a/arch/microblaze/kernel/syscall_table.S b/arch/microblaze/kernel/syscall_table.S
index 29c8568..6e4b0fe 100644
--- a/arch/microblaze/kernel/syscall_table.S
+++ b/arch/microblaze/kernel/syscall_table.S
@@ -389,3 +389,6 @@ ENTRY(sys_call_table)
.long sys_memfd_create
.long sys_bpf
.long sys_execveat
+ .long sys_mlock2
+ .long sys_munlock2 /* 390 */
+ .long sys_munlockall2
diff --git a/arch/mips/include/uapi/asm/mman.h b/arch/mips/include/uapi/asm/mman.h
index cfcb876..67c1cdf 100644
--- a/arch/mips/include/uapi/asm/mman.h
+++ b/arch/mips/include/uapi/asm/mman.h
@@ -62,6 +62,11 @@
#define MCL_CURRENT 1 /* lock all current mappings */
#define MCL_FUTURE 2 /* lock all future mappings */
+/*
+ * Flags for mlock
+ */
+#define MLOCK_LOCKED 0x01 /* Lock and populate the specified range */
+
#define MADV_NORMAL 0 /* no further special treatment */
#define MADV_RANDOM 1 /* expect random page references */
#define MADV_SEQUENTIAL 2 /* expect sequential page references */
diff --git a/arch/mips/include/uapi/asm/unistd.h b/arch/mips/include/uapi/asm/unistd.h
index c03088f..101b884 100644
--- a/arch/mips/include/uapi/asm/unistd.h
+++ b/arch/mips/include/uapi/asm/unistd.h
@@ -377,16 +377,19 @@
#define __NR_memfd_create (__NR_Linux + 354)
#define __NR_bpf (__NR_Linux + 355)
#define __NR_execveat (__NR_Linux + 356)
+#define __NR_mlock2 (__NR_Linux + 357)
+#define __NR_munlock2 (__NR_Linux + 358)
+#define __NR_munlockall2 (__NR_Linux + 359)
/*
* Offset of the last Linux o32 flavoured syscall
*/
-#define __NR_Linux_syscalls 356
+#define __NR_Linux_syscalls 359
#endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */
#define __NR_O32_Linux 4000
-#define __NR_O32_Linux_syscalls 356
+#define __NR_O32_Linux_syscalls 359
#if _MIPS_SIM == _MIPS_SIM_ABI64
@@ -711,16 +714,19 @@
#define __NR_memfd_create (__NR_Linux + 314)
#define __NR_bpf (__NR_Linux + 315)
#define __NR_execveat (__NR_Linux + 316)
+#define __NR_mlock2 (__NR_Linux + 317)
+#define __NR_munlock2 (__NR_Linux + 318)
+#define __NR_munlockall2 (__NR_Linux + 319)
/*
* Offset of the last Linux 64-bit flavoured syscall
*/
-#define __NR_Linux_syscalls 316
+#define __NR_Linux_syscalls 319
#endif /* _MIPS_SIM == _MIPS_SIM_ABI64 */
#define __NR_64_Linux 5000
-#define __NR_64_Linux_syscalls 316
+#define __NR_64_Linux_syscalls 319
#if _MIPS_SIM == _MIPS_SIM_NABI32
@@ -1049,15 +1055,18 @@
#define __NR_memfd_create (__NR_Linux + 318)
#define __NR_bpf (__NR_Linux + 319)
#define __NR_execveat (__NR_Linux + 320)
+#define __NR_mlock2 (__NR_Linux + 321)
+#define __NR_munlock2 (__NR_Linux + 322)
+#define __NR_munlockall2 (__NR_Linux + 323)
/*
* Offset of the last N32 flavoured syscall
*/
-#define __NR_Linux_syscalls 320
+#define __NR_Linux_syscalls 323
#endif /* _MIPS_SIM == _MIPS_SIM_NABI32 */
#define __NR_N32_Linux 6000
-#define __NR_N32_Linux_syscalls 320
+#define __NR_N32_Linux_syscalls 323
#endif /* _UAPI_ASM_UNISTD_H */
diff --git a/arch/mips/kernel/scall32-o32.S b/arch/mips/kernel/scall32-o32.S
index 4cc1350..c409d53 100644
--- a/arch/mips/kernel/scall32-o32.S
+++ b/arch/mips/kernel/scall32-o32.S
@@ -599,3 +599,6 @@ EXPORT(sys_call_table)
PTR sys_memfd_create
PTR sys_bpf /* 4355 */
PTR sys_execveat
+ PTR sys_mlock2
+ PTR sys_munlock2
+ PTR sys_munlockall2
diff --git a/arch/mips/kernel/scall64-64.S b/arch/mips/kernel/scall64-64.S
index ad4d4463..0aa2742 100644
--- a/arch/mips/kernel/scall64-64.S
+++ b/arch/mips/kernel/scall64-64.S
@@ -436,4 +436,7 @@ EXPORT(sys_call_table)
PTR sys_memfd_create
PTR sys_bpf /* 5315 */
PTR sys_execveat
+ PTR sys_mlock2
+ PTR sys_munlock2
+ PTR sys_munlockall2
.size sys_call_table,.-sys_call_table
diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S
index 446cc65..eb21955 100644
--- a/arch/mips/kernel/scall64-n32.S
+++ b/arch/mips/kernel/scall64-n32.S
@@ -429,4 +429,7 @@ EXPORT(sysn32_call_table)
PTR sys_memfd_create
PTR sys_bpf
PTR compat_sys_execveat /* 6320 */
+ PTR sys_mlock2
+ PTR sys_munlock2
+ PTR sys_munlockall2
.size sysn32_call_table,.-sysn32_call_table
diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S
index f543ff4..f45049c 100644
--- a/arch/mips/kernel/scall64-o32.S
+++ b/arch/mips/kernel/scall64-o32.S
@@ -584,4 +584,7 @@ EXPORT(sys32_call_table)
PTR sys_memfd_create
PTR sys_bpf /* 4355 */
PTR compat_sys_execveat
+ PTR sys_mlock2
+ PTR sys_munlock2
+ PTR sys_munlockall2
.size sys32_call_table,.-sys32_call_table
diff --git a/arch/mn10300/kernel/entry.S b/arch/mn10300/kernel/entry.S
index 177d61d..d34adf5 100644
--- a/arch/mn10300/kernel/entry.S
+++ b/arch/mn10300/kernel/entry.S
@@ -767,6 +767,9 @@ ENTRY(sys_call_table)
.long sys_perf_event_open
.long sys_recvmmsg
.long sys_setns
+ .long sys_mlock2 /* 340 */
+ .long sys_munlock2
+ .long sys_munlockall2
nr_syscalls=(.-sys_call_table)/4
diff --git a/arch/parisc/include/uapi/asm/mman.h b/arch/parisc/include/uapi/asm/mman.h
index 294d251..daab994 100644
--- a/arch/parisc/include/uapi/asm/mman.h
+++ b/arch/parisc/include/uapi/asm/mman.h
@@ -32,6 +32,8 @@
#define MCL_CURRENT 1 /* lock all current mappings */
#define MCL_FUTURE 2 /* lock all future mappings */
+#define MLOCK_LOCKED 0x01 /* Lock and populate the specified range */
+
#define MADV_NORMAL 0 /* no further special treatment */
#define MADV_RANDOM 1 /* expect random page references */
#define MADV_SEQUENTIAL 2 /* expect sequential page references */
diff --git a/arch/parisc/include/uapi/asm/unistd.h b/arch/parisc/include/uapi/asm/unistd.h
index 2e639d7..455c8a3 100644
--- a/arch/parisc/include/uapi/asm/unistd.h
+++ b/arch/parisc/include/uapi/asm/unistd.h
@@ -358,8 +358,11 @@
#define __NR_memfd_create (__NR_Linux + 340)
#define __NR_bpf (__NR_Linux + 341)
#define __NR_execveat (__NR_Linux + 342)
+#define __NR_mlock2 (__NR_Linux + 343)
+#define __NR_munlock2 (__NR_Linux + 344)
+#define __NR_munlockall2 (__NR_Linux + 345)
-#define __NR_Linux_syscalls (__NR_execveat + 1)
+#define __NR_Linux_syscalls (__NR_munlockall2 + 1)
#define __IGNORE_select /* newselect */
diff --git a/arch/powerpc/include/uapi/asm/mman.h b/arch/powerpc/include/uapi/asm/mman.h
index 6ea26df..189e85f 100644
--- a/arch/powerpc/include/uapi/asm/mman.h
+++ b/arch/powerpc/include/uapi/asm/mman.h
@@ -23,6 +23,8 @@
#define MCL_CURRENT 0x2000 /* lock all currently mapped pages */
#define MCL_FUTURE 0x4000 /* lock all additions to address space */
+#define MLOCK_LOCKED 0x01 /* Lock and populate the specified range */
+
#define MAP_POPULATE 0x8000 /* populate (prefault) pagetables */
#define MAP_NONBLOCK 0x10000 /* do not block on IO */
#define MAP_STACK 0x20000 /* give out an address that is best suited for process/thread stacks */
diff --git a/arch/powerpc/include/uapi/asm/unistd.h b/arch/powerpc/include/uapi/asm/unistd.h
index e4aa173..c9901e7 100644
--- a/arch/powerpc/include/uapi/asm/unistd.h
+++ b/arch/powerpc/include/uapi/asm/unistd.h
@@ -386,5 +386,8 @@
#define __NR_bpf 361
#define __NR_execveat 362
#define __NR_switch_endian 363
+#define __NR_mlock2 364
+#define __NR_munlock2 365
+#define __NR_munlockall2 366
#endif /* _UAPI_ASM_POWERPC_UNISTD_H_ */
diff --git a/arch/s390/include/uapi/asm/unistd.h b/arch/s390/include/uapi/asm/unistd.h
index 67878af..d1c5b1f 100644
--- a/arch/s390/include/uapi/asm/unistd.h
+++ b/arch/s390/include/uapi/asm/unistd.h
@@ -290,7 +290,10 @@
#define __NR_s390_pci_mmio_write 352
#define __NR_s390_pci_mmio_read 353
#define __NR_execveat 354
-#define NR_syscalls 355
+#define __NR_mlock2 355
+#define __NR_munlock2 356
+#define __NR_munlockall2 357
+#define NR_syscalls 358
/*
* There are some system calls that are not present on 64 bit, some
diff --git a/arch/s390/kernel/compat_wrapper.c b/arch/s390/kernel/compat_wrapper.c
index f8498dd..58339e2 100644
--- a/arch/s390/kernel/compat_wrapper.c
+++ b/arch/s390/kernel/compat_wrapper.c
@@ -220,3 +220,6 @@ COMPAT_SYSCALL_WRAP2(memfd_create, const char __user *, uname, unsigned int, fla
COMPAT_SYSCALL_WRAP3(bpf, int, cmd, union bpf_attr *, attr, unsigned int, size);
COMPAT_SYSCALL_WRAP3(s390_pci_mmio_write, const unsigned long, mmio_addr, const void __user *, user_buffer, const size_t, length);
COMPAT_SYSCALL_WRAP3(s390_pci_mmio_read, const unsigned long, mmio_addr, void __user *, user_buffer, const size_t, length);
+COMPAT_SYSCALL_WRAP3(mlock2, unsigned long, start, size_t, len, int, flags);
+COMPAT_SYSCALL_WRAP3(munlock2, unsigned long, start, size_t, len, int, flags);
+COMPAT_SYSCALL_WRAP1(munlockall2, int, flags);
diff --git a/arch/s390/kernel/syscalls.S b/arch/s390/kernel/syscalls.S
index 1acad02..f6d81d6 100644
--- a/arch/s390/kernel/syscalls.S
+++ b/arch/s390/kernel/syscalls.S
@@ -363,3 +363,6 @@ SYSCALL(sys_bpf,compat_sys_bpf)
SYSCALL(sys_s390_pci_mmio_write,compat_sys_s390_pci_mmio_write)
SYSCALL(sys_s390_pci_mmio_read,compat_sys_s390_pci_mmio_read)
SYSCALL(sys_execveat,compat_sys_execveat)
+SYSCALL(sys_mlock2,compat_sys_mlock2) /* 355 */
+SYSCALL(sys_munlock2,compat_sys_munlock2)
+SYSCALL(sys_munlockall2,compat_sys_munlockall2)
diff --git a/arch/sh/kernel/syscalls_32.S b/arch/sh/kernel/syscalls_32.S
index 734234b..6d07867 100644
--- a/arch/sh/kernel/syscalls_32.S
+++ b/arch/sh/kernel/syscalls_32.S
@@ -386,3 +386,6 @@ ENTRY(sys_call_table)
.long sys_process_vm_writev
.long sys_kcmp
.long sys_finit_module
+ .long sys_mlock2
+ .long sys_munlock2 /* 370 */
+ .long sys_munlockall2
diff --git a/arch/sparc/include/uapi/asm/mman.h b/arch/sparc/include/uapi/asm/mman.h
index 0b14df3..13d51be 100644
--- a/arch/sparc/include/uapi/asm/mman.h
+++ b/arch/sparc/include/uapi/asm/mman.h
@@ -18,6 +18,8 @@
#define MCL_CURRENT 0x2000 /* lock all currently mapped pages */
#define MCL_FUTURE 0x4000 /* lock all additions to address space */
+#define MLOCK_LOCKED 0x01 /* Lock and populate the specified range */
+
#define MAP_POPULATE 0x8000 /* populate (prefault) pagetables */
#define MAP_NONBLOCK 0x10000 /* do not block on IO */
#define MAP_STACK 0x20000 /* give out an address that is best suited for process/thread stacks */
diff --git a/arch/sparc/include/uapi/asm/unistd.h b/arch/sparc/include/uapi/asm/unistd.h
index 6f35f4d..c25bbb1 100644
--- a/arch/sparc/include/uapi/asm/unistd.h
+++ b/arch/sparc/include/uapi/asm/unistd.h
@@ -416,8 +416,11 @@
#define __NR_memfd_create 348
#define __NR_bpf 349
#define __NR_execveat 350
+#define __NR_mlock2 351
+#define __NR_munlock2 352
+#define __NR_munlockall2 353
-#define NR_syscalls 351
+#define NR_syscalls 354
/* Bitmask values returned from kern_features system call. */
#define KERN_FEATURE_MIXED_MODE_STACK 0x00000001
diff --git a/arch/sparc/kernel/systbls_32.S b/arch/sparc/kernel/systbls_32.S
index e31a905..72b68d4 100644
--- a/arch/sparc/kernel/systbls_32.S
+++ b/arch/sparc/kernel/systbls_32.S
@@ -87,4 +87,4 @@ sys_call_table:
/*335*/ .long sys_syncfs, sys_sendmmsg, sys_setns, sys_process_vm_readv, sys_process_vm_writev
/*340*/ .long sys_ni_syscall, sys_kcmp, sys_finit_module, sys_sched_setattr, sys_sched_getattr
/*345*/ .long sys_renameat2, sys_seccomp, sys_getrandom, sys_memfd_create, sys_bpf
-/*350*/ .long sys_execveat
+/*350*/ .long sys_execveat, sys_mlock2, sys_munlock2, sys_munlockall2
diff --git a/arch/sparc/kernel/systbls_64.S b/arch/sparc/kernel/systbls_64.S
index d72f76a..a96bfea 100644
--- a/arch/sparc/kernel/systbls_64.S
+++ b/arch/sparc/kernel/systbls_64.S
@@ -88,7 +88,7 @@ sys_call_table32:
.word sys_syncfs, compat_sys_sendmmsg, sys_setns, compat_sys_process_vm_readv, compat_sys_process_vm_writev
/*340*/ .word sys_kern_features, sys_kcmp, sys_finit_module, sys_sched_setattr, sys_sched_getattr
.word sys32_renameat2, sys_seccomp, sys_getrandom, sys_memfd_create, sys_bpf
-/*350*/ .word sys32_execveat
+/*350*/ .word sys32_execveat, sys_mlock2, sys_munlock2, sys_munlockall2
#endif /* CONFIG_COMPAT */
@@ -168,4 +168,4 @@ sys_call_table:
.word sys_syncfs, sys_sendmmsg, sys_setns, sys_process_vm_readv, sys_process_vm_writev
/*340*/ .word sys_kern_features, sys_kcmp, sys_finit_module, sys_sched_setattr, sys_sched_getattr
.word sys_renameat2, sys_seccomp, sys_getrandom, sys_memfd_create, sys_bpf
-/*350*/ .word sys64_execveat
+/*350*/ .word sys64_execveat, sys_mlock2, sys_munlock2, sys_munlockall2
diff --git a/arch/tile/include/uapi/asm/mman.h b/arch/tile/include/uapi/asm/mman.h
index 81b8fc3..f69ce48 100644
--- a/arch/tile/include/uapi/asm/mman.h
+++ b/arch/tile/include/uapi/asm/mman.h
@@ -37,5 +37,10 @@
#define MCL_CURRENT 1 /* lock all current mappings */
#define MCL_FUTURE 2 /* lock all future mappings */
+/*
+ * Flags for mlock
+ */
+#define MLOCK_LOCKED 0x01 /* Lock and populate the specified range */
+
#endif /* _ASM_TILE_MMAN_H */
diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
index ef8187f..13ce950 100644
--- a/arch/x86/entry/syscalls/syscall_32.tbl
+++ b/arch/x86/entry/syscalls/syscall_32.tbl
@@ -365,3 +365,6 @@
356 i386 memfd_create sys_memfd_create
357 i386 bpf sys_bpf
358 i386 execveat sys_execveat stub32_execveat
+359 i386 mlock2 sys_mlock2
+360 i386 munlock2 sys_munlock2
+361 i386 munlockall2 sys_munlockall2
diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
index 9ef32d5..13b3cb1 100644
--- a/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/arch/x86/entry/syscalls/syscall_64.tbl
@@ -329,6 +329,9 @@
320 common kexec_file_load sys_kexec_file_load
321 common bpf sys_bpf
322 64 execveat stub_execveat
+323 common mlock2 sys_mlock2
+324 common munlock2 sys_munlock2
+325 common munlockall2 sys_munlockall2
#
# x32-specific system call numbers start at 512 to avoid cache impact
diff --git a/arch/xtensa/include/uapi/asm/mman.h b/arch/xtensa/include/uapi/asm/mman.h
index 201aec0..11f354f 100644
--- a/arch/xtensa/include/uapi/asm/mman.h
+++ b/arch/xtensa/include/uapi/asm/mman.h
@@ -75,6 +75,11 @@
#define MCL_CURRENT 1 /* lock all current mappings */
#define MCL_FUTURE 2 /* lock all future mappings */
+/*
+ * Flags for mlock
+ */
+#define MLOCK_LOCKED 0x01 /* Lock and populate the specified range */
+
#define MADV_NORMAL 0 /* no further special treatment */
#define MADV_RANDOM 1 /* expect random page references */
#define MADV_SEQUENTIAL 2 /* expect sequential page references */
diff --git a/arch/xtensa/include/uapi/asm/unistd.h b/arch/xtensa/include/uapi/asm/unistd.h
index b95c305..fbd0876 100644
--- a/arch/xtensa/include/uapi/asm/unistd.h
+++ b/arch/xtensa/include/uapi/asm/unistd.h
@@ -753,8 +753,14 @@ __SYSCALL(339, sys_memfd_create, 2)
__SYSCALL(340, sys_bpf, 3)
#define __NR_execveat 341
__SYSCALL(341, sys_execveat, 5)
-
-#define __NR_syscall_count 342
+#define __NR_mlock2 342
+__SYSCALL(342, sys_mlock2, 3)
+#define __NR_munlock2 343
+__SYSCALL(343, sys_munlock2, 3)
+#define __NR_munlockall2 344
+__SYSCALL(344, sys_munlock2, 1)
+
+#define __NR_syscall_count 345
/*
* sysxtensa syscall handler
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index b45c45b..aecab5d 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -884,4 +884,8 @@ asmlinkage long sys_execveat(int dfd, const char __user *filename,
const char __user *const __user *argv,
const char __user *const __user *envp, int flags);
+asmlinkage long sys_mlock2(unsigned long start, size_t len, int flags);
+asmlinkage long sys_munlock2(unsigned long start, size_t len, int flags);
+asmlinkage long sys_munlockall2(int flags);
+
#endif
diff --git a/include/uapi/asm-generic/mman.h b/include/uapi/asm-generic/mman.h
index e9fe6fd..242436b 100644
--- a/include/uapi/asm-generic/mman.h
+++ b/include/uapi/asm-generic/mman.h
@@ -18,4 +18,6 @@
#define MCL_CURRENT 1 /* lock all current mappings */
#define MCL_FUTURE 2 /* lock all future mappings */
+#define MLOCK_LOCKED 0x01 /* Lock and populate the specified range */
+
#endif /* __ASM_GENERIC_MMAN_H */
diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h
index e016bd9..e759fa2 100644
--- a/include/uapi/asm-generic/unistd.h
+++ b/include/uapi/asm-generic/unistd.h
@@ -709,9 +709,15 @@ __SYSCALL(__NR_memfd_create, sys_memfd_create)
__SYSCALL(__NR_bpf, sys_bpf)
#define __NR_execveat 281
__SC_COMP(__NR_execveat, sys_execveat, compat_sys_execveat)
+#define __NR_mlock2 282
+__SYSCALL(__NR_mlock2, sys_mlock2)
+#define __NR_munlock2 283
+__SYSCALL(__NR_munlock2, sys_munlock2)
+#define __NR_munlockall2 284
+__SYSCALL(__NR_munlockall2, sys_munlockall2)
#undef __NR_syscalls
-#define __NR_syscalls 282
+#define __NR_syscalls 285
/*
* All syscalls below here should go away really,
diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c
index 7995ef5..63529b7 100644
--- a/kernel/sys_ni.c
+++ b/kernel/sys_ni.c
@@ -193,6 +193,9 @@ cond_syscall(sys_mlock);
cond_syscall(sys_munlock);
cond_syscall(sys_mlockall);
cond_syscall(sys_munlockall);
+cond_syscall(sys_mlock2);
+cond_syscall(sys_munlock2);
+cond_syscall(sys_munlockall2);
cond_syscall(sys_mincore);
cond_syscall(sys_madvise);
cond_syscall(sys_mremap);
diff --git a/mm/mlock.c b/mm/mlock.c
index 8e52c23..d6e61d6 100644
--- a/mm/mlock.c
+++ b/mm/mlock.c
@@ -648,6 +648,14 @@ SYSCALL_DEFINE2(mlock, unsigned long, start, size_t, len)
return do_mlock(start, len, VM_LOCKED);
}
+SYSCALL_DEFINE3(mlock2, unsigned long, start, size_t, len, int, flags)
+{
+ if (!flags || flags & ~MLOCK_LOCKED)
+ return -EINVAL;
+
+ return do_mlock(start, len, VM_LOCKED);
+}
+
static int do_munlock(unsigned long start, size_t len, vm_flags_t flags)
{
int ret;
@@ -667,6 +675,13 @@ SYSCALL_DEFINE2(munlock, unsigned long, start, size_t, len)
return do_munlock(start, len, VM_LOCKED);
}
+SYSCALL_DEFINE3(munlock2, unsigned long, start, size_t, len, int, flags)
+{
+ if (!flags || flags & ~MLOCK_LOCKED)
+ return -EINVAL;
+ return do_munlock(start, len, VM_LOCKED);
+}
+
static int do_mlockall(int flags)
{
struct vm_area_struct * vma, * prev = NULL;
@@ -756,6 +771,19 @@ SYSCALL_DEFINE0(munlockall)
return ret;
}
+SYSCALL_DEFINE1(munlockall2, int, flags)
+{
+ int ret = -EINVAL;
+
+ if (!flags || flags & ~(MCL_CURRENT | MCL_FUTURE))
+ return ret;
+
+ down_write(¤t->mm->mmap_sem);
+ ret = do_munlockall(flags);
+ up_write(¤t->mm->mmap_sem);
+ return ret;
+}
+
/*
* Objects with different lifetime than processes (SHM_LOCK and SHM_HUGETLB
* shm segments) get accounted against the user_struct instead.
--
1.9.1
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply related
* [PATCH V4 4/6] mm: mlock: Introduce VM_LOCKONFAULT and add mlock flags to enable it
From: Eric B Munson @ 2015-07-21 19:59 UTC (permalink / raw)
To: Andrew Morton
Cc: Eric B Munson, Michal Hocko, Vlastimil Babka, Jonathan Corbet,
linux-alpha, linux-kernel, linux-mips, linux-parisc, linuxppc-dev,
sparclinux, linux-xtensa, dri-devel, linux-mm, linux-arch,
linux-api
In-Reply-To: <1437508781-28655-1-git-send-email-emunson@akamai.com>
The cost of faulting in all memory to be locked can be very high when
working with large mappings. If only portions of the mapping will be
used this can incur a high penalty for locking.
For the example of a large file, this is the usage pattern for a large
statical language model (probably applies to other statical or graphical
models as well). For the security example, any application transacting
in data that cannot be swapped out (credit card data, medical records,
etc).
This patch introduces the ability to request that pages are not
pre-faulted, but are placed on the unevictable LRU when they are finally
faulted in. This can be done area at a time via the
mlock2(MLOCK_ONFAULT) or the mlockall(MCL_ONFAULT) system calls. These
calls can be undone via munlock2(MLOCK_ONFAULT) or
munlockall2(MCL_ONFAULT).
Applying the VM_LOCKONFAULT flag to a mapping with pages that are
already present required the addition of a function in gup.c to pin all
pages which are present in an address range. It borrows heavily from
__mm_populate().
To keep accounting checks out of the page fault path, users are billed
for the entire mapping lock as if MLOCK_LOCKED was used.
Signed-off-by: Eric B Munson <emunson@akamai.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: linux-alpha@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-mips@linux-mips.org
Cc: linux-parisc@vger.kernel.org
Cc: linuxppc-dev@lists.ozlabs.org
Cc: sparclinux@vger.kernel.org
Cc: linux-xtensa@linux-xtensa.org
Cc: dri-devel@lists.freedesktop.org
Cc: linux-mm@kvack.org
Cc: linux-arch@vger.kernel.org
Cc: linux-api@vger.kernel.org
---
Changes from V3:
Do extensive search for VM_LOCKED and ensure that VM_LOCKONFAULT is also handled
where appropriate
arch/alpha/include/uapi/asm/mman.h | 2 +
arch/mips/include/uapi/asm/mman.h | 2 +
arch/parisc/include/uapi/asm/mman.h | 2 +
arch/powerpc/include/uapi/asm/mman.h | 2 +
arch/sparc/include/uapi/asm/mman.h | 2 +
arch/tile/include/uapi/asm/mman.h | 3 ++
arch/xtensa/include/uapi/asm/mman.h | 2 +
drivers/gpu/drm/drm_vm.c | 8 ++-
fs/proc/task_mmu.c | 3 +-
include/linux/mm.h | 2 +
include/uapi/asm-generic/mman.h | 2 +
kernel/events/uprobes.c | 2 +-
kernel/fork.c | 2 +-
mm/debug.c | 1 +
mm/gup.c | 3 +-
mm/huge_memory.c | 3 +-
mm/hugetlb.c | 4 +-
mm/internal.h | 5 +-
mm/ksm.c | 2 +-
mm/madvise.c | 4 +-
mm/memory.c | 5 +-
mm/mlock.c | 98 +++++++++++++++++++++++++-----------
mm/mmap.c | 28 +++++++----
mm/mremap.c | 6 +--
mm/msync.c | 2 +-
mm/rmap.c | 12 ++---
mm/shmem.c | 2 +-
mm/swap.c | 3 +-
mm/vmscan.c | 2 +-
29 files changed, 145 insertions(+), 69 deletions(-)
diff --git a/arch/alpha/include/uapi/asm/mman.h b/arch/alpha/include/uapi/asm/mman.h
index ec72436..77ae8db 100644
--- a/arch/alpha/include/uapi/asm/mman.h
+++ b/arch/alpha/include/uapi/asm/mman.h
@@ -37,8 +37,10 @@
#define MCL_CURRENT 8192 /* lock all currently mapped pages */
#define MCL_FUTURE 16384 /* lock all additions to address space */
+#define MCL_ONFAULT 32768 /* lock all pages that are faulted in */
#define MLOCK_LOCKED 0x01 /* Lock and populate the specified range */
+#define MLOCK_ONFAULT 0x02 /* Lock pages in range after they are faulted in, do not prefault */
#define MADV_NORMAL 0 /* no further special treatment */
#define MADV_RANDOM 1 /* expect random page references */
diff --git a/arch/mips/include/uapi/asm/mman.h b/arch/mips/include/uapi/asm/mman.h
index 67c1cdf..71ed81d 100644
--- a/arch/mips/include/uapi/asm/mman.h
+++ b/arch/mips/include/uapi/asm/mman.h
@@ -61,11 +61,13 @@
*/
#define MCL_CURRENT 1 /* lock all current mappings */
#define MCL_FUTURE 2 /* lock all future mappings */
+#define MCL_ONFAULT 4 /* lock all pages that are faulted in */
/*
* Flags for mlock
*/
#define MLOCK_LOCKED 0x01 /* Lock and populate the specified range */
+#define MLOCK_ONFAULT 0x02 /* Lock pages in range after they are faulted in, do not prefault */
#define MADV_NORMAL 0 /* no further special treatment */
#define MADV_RANDOM 1 /* expect random page references */
diff --git a/arch/parisc/include/uapi/asm/mman.h b/arch/parisc/include/uapi/asm/mman.h
index daab994..c0871ce 100644
--- a/arch/parisc/include/uapi/asm/mman.h
+++ b/arch/parisc/include/uapi/asm/mman.h
@@ -31,8 +31,10 @@
#define MCL_CURRENT 1 /* lock all current mappings */
#define MCL_FUTURE 2 /* lock all future mappings */
+#define MCL_ONFAULT 4 /* lock all pages that are faulted in */
#define MLOCK_LOCKED 0x01 /* Lock and populate the specified range */
+#define MLOCK_ONFAULT 0x02 /* Lock pages in range after they are faulted in, do not prefault */
#define MADV_NORMAL 0 /* no further special treatment */
#define MADV_RANDOM 1 /* expect random page references */
diff --git a/arch/powerpc/include/uapi/asm/mman.h b/arch/powerpc/include/uapi/asm/mman.h
index 189e85f..f93f7eb 100644
--- a/arch/powerpc/include/uapi/asm/mman.h
+++ b/arch/powerpc/include/uapi/asm/mman.h
@@ -22,8 +22,10 @@
#define MCL_CURRENT 0x2000 /* lock all currently mapped pages */
#define MCL_FUTURE 0x4000 /* lock all additions to address space */
+#define MCL_ONFAULT 0x8000 /* lock all pages that are faulted in */
#define MLOCK_LOCKED 0x01 /* Lock and populate the specified range */
+#define MLOCK_ONFAULT 0x02 /* Lock pages in range after they are faulted in, do not prefault */
#define MAP_POPULATE 0x8000 /* populate (prefault) pagetables */
#define MAP_NONBLOCK 0x10000 /* do not block on IO */
diff --git a/arch/sparc/include/uapi/asm/mman.h b/arch/sparc/include/uapi/asm/mman.h
index 13d51be..8cd2ebc 100644
--- a/arch/sparc/include/uapi/asm/mman.h
+++ b/arch/sparc/include/uapi/asm/mman.h
@@ -17,8 +17,10 @@
#define MCL_CURRENT 0x2000 /* lock all currently mapped pages */
#define MCL_FUTURE 0x4000 /* lock all additions to address space */
+#define MCL_ONFAULT 0x8000 /* lock all pages that are faulted in */
#define MLOCK_LOCKED 0x01 /* Lock and populate the specified range */
+#define MLOCK_ONFAULT 0x02 /* Lock pages in range after they are faulted in, do not prefault */
#define MAP_POPULATE 0x8000 /* populate (prefault) pagetables */
#define MAP_NONBLOCK 0x10000 /* do not block on IO */
diff --git a/arch/tile/include/uapi/asm/mman.h b/arch/tile/include/uapi/asm/mman.h
index f69ce48..acdd013 100644
--- a/arch/tile/include/uapi/asm/mman.h
+++ b/arch/tile/include/uapi/asm/mman.h
@@ -36,11 +36,14 @@
*/
#define MCL_CURRENT 1 /* lock all current mappings */
#define MCL_FUTURE 2 /* lock all future mappings */
+#define MCL_ONFAULT 4 /* lock all pages that are faulted in */
+
/*
* Flags for mlock
*/
#define MLOCK_LOCKED 0x01 /* Lock and populate the specified range */
+#define MLOCK_ONFAULT 0x02 /* Lock pages in range after they are faulted in, do not prefault */
#endif /* _ASM_TILE_MMAN_H */
diff --git a/arch/xtensa/include/uapi/asm/mman.h b/arch/xtensa/include/uapi/asm/mman.h
index 11f354f..5725a15 100644
--- a/arch/xtensa/include/uapi/asm/mman.h
+++ b/arch/xtensa/include/uapi/asm/mman.h
@@ -74,11 +74,13 @@
*/
#define MCL_CURRENT 1 /* lock all current mappings */
#define MCL_FUTURE 2 /* lock all future mappings */
+#define MCL_ONFAULT 4 /* lock all pages that are faulted in */
/*
* Flags for mlock
*/
#define MLOCK_LOCKED 0x01 /* Lock and populate the specified range */
+#define MLOCK_ONFAULT 0x02 /* Lock pages in range after they are faulted in, do not prefault */
#define MADV_NORMAL 0 /* no further special treatment */
#define MADV_RANDOM 1 /* expect random page references */
diff --git a/drivers/gpu/drm/drm_vm.c b/drivers/gpu/drm/drm_vm.c
index aab49ee..dfbcfc2 100644
--- a/drivers/gpu/drm/drm_vm.c
+++ b/drivers/gpu/drm/drm_vm.c
@@ -699,9 +699,15 @@ int drm_vma_info(struct seq_file *m, void *data)
(void *)(unsigned long)virt_to_phys(high_memory));
list_for_each_entry(pt, &dev->vmalist, head) {
+ char lock_flag = '-';
+
vma = pt->vma;
if (!vma)
continue;
+ if (vma->vm_flags & VM_LOCKED)
+ lock_flag = 'l';
+ else if (vma->vm_flags & VM_LOCKONFAULT)
+ lock_flag = 'f';
seq_printf(m,
"\n%5d 0x%pK-0x%pK %c%c%c%c%c%c 0x%08lx000",
pt->pid,
@@ -710,7 +716,7 @@ int drm_vma_info(struct seq_file *m, void *data)
vma->vm_flags & VM_WRITE ? 'w' : '-',
vma->vm_flags & VM_EXEC ? 'x' : '-',
vma->vm_flags & VM_MAYSHARE ? 's' : 'p',
- vma->vm_flags & VM_LOCKED ? 'l' : '-',
+ lock_flag,
vma->vm_flags & VM_IO ? 'i' : '-',
vma->vm_pgoff);
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index ca1e091..2c435a7 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -579,6 +579,7 @@ static void show_smap_vma_flags(struct seq_file *m, struct vm_area_struct *vma)
#ifdef CONFIG_X86_INTEL_MPX
[ilog2(VM_MPX)] = "mp",
#endif
+ [ilog2(VM_LOCKONFAULT)] = "lf",
[ilog2(VM_LOCKED)] = "lo",
[ilog2(VM_IO)] = "io",
[ilog2(VM_SEQ_READ)] = "sr",
@@ -654,7 +655,7 @@ static int show_smap(struct seq_file *m, void *v, int is_pid)
mss.swap >> 10,
vma_kernel_pagesize(vma) >> 10,
vma_mmu_pagesize(vma) >> 10,
- (vma->vm_flags & VM_LOCKED) ?
+ (vma->vm_flags & (VM_LOCKED | VM_LOCKONFAULT)) ?
(unsigned long)(mss.pss >> (10 + PSS_SHIFT)) : 0);
show_smap_vma_flags(m, vma);
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 2e872f9..e78544f 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -127,6 +127,7 @@ extern unsigned int kobjsize(const void *objp);
#define VM_PFNMAP 0x00000400 /* Page-ranges managed without "struct page", just pure PFN */
#define VM_DENYWRITE 0x00000800 /* ETXTBSY on write attempts.. */
+#define VM_LOCKONFAULT 0x00001000 /* Lock the pages covered when they are faulted in */
#define VM_LOCKED 0x00002000
#define VM_IO 0x00004000 /* Memory mapped I/O or similar */
@@ -1865,6 +1866,7 @@ static inline void mm_populate(unsigned long addr, unsigned long len)
/* Ignore errors */
(void) __mm_populate(addr, len, 1);
}
+extern int mm_lock_present(unsigned long addr, unsigned long start);
#else
static inline void mm_populate(unsigned long addr, unsigned long len) {}
#endif
diff --git a/include/uapi/asm-generic/mman.h b/include/uapi/asm-generic/mman.h
index 242436b..555aab0 100644
--- a/include/uapi/asm-generic/mman.h
+++ b/include/uapi/asm-generic/mman.h
@@ -17,7 +17,9 @@
#define MCL_CURRENT 1 /* lock all current mappings */
#define MCL_FUTURE 2 /* lock all future mappings */
+#define MCL_ONFAULT 4 /* lock all pages that are faulted in */
#define MLOCK_LOCKED 0x01 /* Lock and populate the specified range */
+#define MLOCK_ONFAULT 0x02 /* Lock pages in range after they are faulted in, do not prefault */
#endif /* __ASM_GENERIC_MMAN_H */
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index cb346f2..882c9f6 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -201,7 +201,7 @@ static int __replace_page(struct vm_area_struct *vma, unsigned long addr,
try_to_free_swap(page);
pte_unmap_unlock(ptep, ptl);
- if (vma->vm_flags & VM_LOCKED)
+ if (vma->vm_flags & (VM_LOCKED | VM_LOCKONFAULT))
munlock_vma_page(page);
put_page(page);
diff --git a/kernel/fork.c b/kernel/fork.c
index dbd9b8d..a949228 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -454,7 +454,7 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
tmp->vm_mm = mm;
if (anon_vma_fork(tmp, mpnt))
goto fail_nomem_anon_vma_fork;
- tmp->vm_flags &= ~VM_LOCKED;
+ tmp->vm_flags &= ~(VM_LOCKED | VM_LOCKONFAULT);
tmp->vm_next = tmp->vm_prev = NULL;
file = tmp->vm_file;
if (file) {
diff --git a/mm/debug.c b/mm/debug.c
index 76089dd..25176bb 100644
--- a/mm/debug.c
+++ b/mm/debug.c
@@ -121,6 +121,7 @@ static const struct trace_print_flags vmaflags_names[] = {
{VM_GROWSDOWN, "growsdown" },
{VM_PFNMAP, "pfnmap" },
{VM_DENYWRITE, "denywrite" },
+ {VM_LOCKONFAULT, "lockonfault" },
{VM_LOCKED, "locked" },
{VM_IO, "io" },
{VM_SEQ_READ, "seqread" },
diff --git a/mm/gup.c b/mm/gup.c
index 233ef17..097a22a 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -92,7 +92,8 @@ retry:
*/
mark_page_accessed(page);
}
- if ((flags & FOLL_POPULATE) && (vma->vm_flags & VM_LOCKED)) {
+ if ((flags & FOLL_POPULATE) &&
+ (vma->vm_flags & (VM_LOCKED | VM_LOCKONFAULT))) {
/*
* The preliminary mapping check is mainly to avoid the
* pointless overhead of lock_page on the ZERO_PAGE
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index c107094..7985e35 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -1238,7 +1238,8 @@ struct page *follow_trans_huge_pmd(struct vm_area_struct *vma,
pmd, _pmd, 1))
update_mmu_cache_pmd(vma, addr, pmd);
}
- if ((flags & FOLL_POPULATE) && (vma->vm_flags & VM_LOCKED)) {
+ if ((flags & FOLL_POPULATE) &&
+ (vma->vm_flags & (VM_LOCKED | VM_LOCKONFAULT))) {
if (page->mapping && trylock_page(page)) {
lru_add_drain();
if (page->mapping)
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index a8c3087..82caa48 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -3764,8 +3764,8 @@ static unsigned long page_table_shareable(struct vm_area_struct *svma,
unsigned long s_end = sbase + PUD_SIZE;
/* Allow segments to share if only one is marked locked */
- unsigned long vm_flags = vma->vm_flags & ~VM_LOCKED;
- unsigned long svm_flags = svma->vm_flags & ~VM_LOCKED;
+ unsigned long vm_flags = vma->vm_flags & ~(VM_LOCKED | VM_LOCKONFAULT);
+ unsigned long svm_flags = svma->vm_flags & ~(VM_LOCKED | VM_LOCKONFAULT);
/*
* match the virtual addresses, permission and the alignment of the
diff --git a/mm/internal.h b/mm/internal.h
index 36b23f1..53e140e 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -246,10 +246,11 @@ void __vma_link_list(struct mm_struct *mm, struct vm_area_struct *vma,
extern long populate_vma_page_range(struct vm_area_struct *vma,
unsigned long start, unsigned long end, int *nonblocking);
extern void munlock_vma_pages_range(struct vm_area_struct *vma,
- unsigned long start, unsigned long end);
+ unsigned long start, unsigned long end, vm_flags_t to_drop);
static inline void munlock_vma_pages_all(struct vm_area_struct *vma)
{
- munlock_vma_pages_range(vma, vma->vm_start, vma->vm_end);
+ munlock_vma_pages_range(vma, vma->vm_start, vma->vm_end,
+ VM_LOCKED | VM_LOCKONFAULT);
}
/*
diff --git a/mm/ksm.c b/mm/ksm.c
index 7ee101e..5d91b7d 100644
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -1058,7 +1058,7 @@ static int try_to_merge_one_page(struct vm_area_struct *vma,
err = replace_page(vma, page, kpage, orig_pte);
}
- if ((vma->vm_flags & VM_LOCKED) && kpage && !err) {
+ if ((vma->vm_flags & (VM_LOCKED | VM_LOCKONFAULT)) && kpage && !err) {
munlock_vma_page(page);
if (!PageMlocked(kpage)) {
unlock_page(page);
diff --git a/mm/madvise.c b/mm/madvise.c
index 64bb8a2..c9d9296 100644
--- a/mm/madvise.c
+++ b/mm/madvise.c
@@ -279,7 +279,7 @@ static long madvise_dontneed(struct vm_area_struct *vma,
unsigned long start, unsigned long end)
{
*prev = vma;
- if (vma->vm_flags & (VM_LOCKED|VM_HUGETLB|VM_PFNMAP))
+ if (vma->vm_flags & (VM_LOCKED|VM_LOCKONFAULT|VM_HUGETLB|VM_PFNMAP))
return -EINVAL;
zap_page_range(vma, start, end - start, NULL);
@@ -300,7 +300,7 @@ static long madvise_remove(struct vm_area_struct *vma,
*prev = NULL; /* tell sys_madvise we drop mmap_sem */
- if (vma->vm_flags & (VM_LOCKED | VM_HUGETLB))
+ if (vma->vm_flags & (VM_LOCKED | VM_LOCKONFAULT | VM_HUGETLB))
return -EINVAL;
f = vma->vm_file;
diff --git a/mm/memory.c b/mm/memory.c
index 388dcf9..2b19e0b 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -2165,7 +2165,7 @@ static int wp_page_copy(struct mm_struct *mm, struct vm_area_struct *vma,
* Don't let another task, with possibly unlocked vma,
* keep the mlocked page.
*/
- if (page_copied && (vma->vm_flags & VM_LOCKED)) {
+ if (page_copied && (vma->vm_flags & (VM_LOCKED | VM_LOCKONFAULT))) {
lock_page(old_page); /* LRU manipulation */
munlock_vma_page(old_page);
unlock_page(old_page);
@@ -2577,7 +2577,8 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma,
}
swap_free(entry);
- if (vm_swap_full() || (vma->vm_flags & VM_LOCKED) || PageMlocked(page))
+ if (vm_swap_full() || (vma->vm_flags & (VM_LOCKED | VM_LOCKONFAULT)) ||
+ PageMlocked(page))
try_to_free_swap(page);
unlock_page(page);
if (page != swapcache) {
diff --git a/mm/mlock.c b/mm/mlock.c
index d6e61d6..8b45be1 100644
--- a/mm/mlock.c
+++ b/mm/mlock.c
@@ -406,23 +406,22 @@ static unsigned long __munlock_pagevec_fill(struct pagevec *pvec,
* @vma - vma containing range to be munlock()ed.
* @start - start address in @vma of the range
* @end - end of range in @vma.
+ * @to_drop - the VMA flags we want to drop from the specified range
*
- * For mremap(), munmap() and exit().
+ * For mremap(), munmap(), munlock(), and exit().
*
- * Called with @vma VM_LOCKED.
- *
- * Returns with VM_LOCKED cleared. Callers must be prepared to
+ * Returns with specified flags cleared. Callers must be prepared to
* deal with this.
*
- * We don't save and restore VM_LOCKED here because pages are
+ * We don't save and restore specified flags here because pages are
* still on lru. In unmap path, pages might be scanned by reclaim
* and re-mlocked by try_to_{munlock|unmap} before we unmap and
* free them. This will result in freeing mlocked pages.
*/
-void munlock_vma_pages_range(struct vm_area_struct *vma,
- unsigned long start, unsigned long end)
+void munlock_vma_pages_range(struct vm_area_struct *vma, unsigned long start,
+ unsigned long end, vm_flags_t to_drop)
{
- vma->vm_flags &= ~VM_LOCKED;
+ vma->vm_flags &= ~to_drop;
while (start < end) {
struct page *page = NULL;
@@ -502,11 +501,12 @@ static int mlock_fixup(struct vm_area_struct *vma, struct vm_area_struct **prev,
pgoff_t pgoff;
int nr_pages;
int ret = 0;
- int lock = !!(newflags & VM_LOCKED);
+ int lock = !!(newflags & (VM_LOCKED | VM_LOCKONFAULT));
if (newflags == vma->vm_flags || (vma->vm_flags & VM_SPECIAL) ||
is_vm_hugetlb_page(vma) || vma == get_gate_vma(current->mm))
- goto out; /* don't set VM_LOCKED, don't count */
+ /* don't set VM_LOCKED or VM_LOCKONFAULT and don't count */
+ goto out;
pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);
*prev = vma_merge(mm, *prev, start, end, newflags, vma->anon_vma,
@@ -546,7 +546,11 @@ success:
if (lock)
vma->vm_flags = newflags;
else
- munlock_vma_pages_range(vma, start, end);
+ /*
+ * We need to tell which VM_LOCK* flag(s) we are clearing here
+ */
+ munlock_vma_pages_range(vma, start, end,
+ (vma->vm_flags & ~(newflags)));
out:
*prev = vma;
@@ -581,10 +585,12 @@ static int apply_vma_flags(unsigned long start, size_t len,
/* Here we know that vma->vm_start <= nstart < vma->vm_end. */
newflags = vma->vm_flags;
- if (add_flags)
+ if (add_flags) {
+ newflags &= ~(VM_LOCKED | VM_LOCKONFAULT);
newflags |= flags;
- else
+ } else {
newflags &= ~flags;
+ }
tmp = vma->vm_end;
if (tmp > end)
@@ -637,9 +643,15 @@ static int do_mlock(unsigned long start, size_t len, vm_flags_t flags)
if (error)
return error;
- error = __mm_populate(start, len, 0);
- if (error)
- return __mlock_posix_error_return(error);
+ if (flags & (VM_LOCKED | VM_LOCKONFAULT)) {
+ if (flags & VM_LOCKED)
+ error = __mm_populate(start, len, 0);
+ else
+ error = mm_lock_present(start, len);
+ if (error)
+ return __mlock_posix_error_return(error);
+ }
+
return 0;
}
@@ -650,10 +662,14 @@ SYSCALL_DEFINE2(mlock, unsigned long, start, size_t, len)
SYSCALL_DEFINE3(mlock2, unsigned long, start, size_t, len, int, flags)
{
- if (!flags || flags & ~MLOCK_LOCKED)
+ if (!flags || (flags & ~(MLOCK_LOCKED | MLOCK_ONFAULT)) ||
+ flags == (MLOCK_LOCKED | MLOCK_ONFAULT))
return -EINVAL;
- return do_mlock(start, len, VM_LOCKED);
+ if (flags & MLOCK_LOCKED)
+ return do_mlock(start, len, VM_LOCKED);
+
+ return do_mlock(start, len, VM_LOCKONFAULT);
}
static int do_munlock(unsigned long start, size_t len, vm_flags_t flags)
@@ -672,31 +688,46 @@ static int do_munlock(unsigned long start, size_t len, vm_flags_t flags)
SYSCALL_DEFINE2(munlock, unsigned long, start, size_t, len)
{
- return do_munlock(start, len, VM_LOCKED);
+ return do_munlock(start, len, VM_LOCKED | VM_LOCKONFAULT);
}
SYSCALL_DEFINE3(munlock2, unsigned long, start, size_t, len, int, flags)
{
- if (!flags || flags & ~MLOCK_LOCKED)
+ vm_flags_t to_clear = 0;
+
+ if (!flags || flags & ~(MLOCK_LOCKED | MLOCK_ONFAULT))
return -EINVAL;
- return do_munlock(start, len, VM_LOCKED);
+
+ if (flags & MLOCK_LOCKED)
+ to_clear |= VM_LOCKED;
+ if (flags & MLOCK_ONFAULT)
+ to_clear |= VM_LOCKONFAULT;
+
+ return do_munlock(start, len, to_clear);
}
static int do_mlockall(int flags)
{
struct vm_area_struct * vma, * prev = NULL;
+ vm_flags_t to_add;
if (flags & MCL_FUTURE)
current->mm->def_flags |= VM_LOCKED;
if (flags == MCL_FUTURE)
goto out;
+ if (flags & MCL_ONFAULT) {
+ current->mm->def_flags |= VM_LOCKONFAULT;
+ to_add = VM_LOCKONFAULT;
+ } else {
+ to_add = VM_LOCKED;
+ }
+
for (vma = current->mm->mmap; vma ; vma = prev->vm_next) {
vm_flags_t newflags;
- newflags = vma->vm_flags & ~VM_LOCKED;
- if (flags & MCL_CURRENT)
- newflags |= VM_LOCKED;
+ newflags = vma->vm_flags & ~(VM_LOCKED | VM_LOCKONFAULT);
+ newflags |= to_add;
/* Ignore errors */
mlock_fixup(vma, &prev, vma->vm_start, vma->vm_end, newflags);
@@ -711,7 +742,8 @@ SYSCALL_DEFINE1(mlockall, int, flags)
unsigned long lock_limit;
int ret = -EINVAL;
- if (!flags || (flags & ~(MCL_CURRENT | MCL_FUTURE)))
+ if (!flags || (flags & ~(MCL_CURRENT | MCL_FUTURE | MCL_ONFAULT)) ||
+ (flags & (MCL_FUTURE | MCL_ONFAULT)) == (MCL_FUTURE | MCL_ONFAULT))
goto out;
ret = -EPERM;
@@ -740,18 +772,24 @@ out:
static int do_munlockall(int flags)
{
struct vm_area_struct * vma, * prev = NULL;
+ vm_flags_t to_clear = 0;
if (flags & MCL_FUTURE)
current->mm->def_flags &= ~VM_LOCKED;
+ if (flags & MCL_ONFAULT)
+ current->mm->def_flags &= ~VM_LOCKONFAULT;
if (flags == MCL_FUTURE)
goto out;
+ if (flags & MCL_CURRENT)
+ to_clear |= VM_LOCKED;
+ if (flags & MCL_ONFAULT)
+ to_clear |= VM_LOCKONFAULT;
+
for (vma = current->mm->mmap; vma ; vma = prev->vm_next) {
vm_flags_t newflags;
- newflags = vma->vm_flags;
- if (flags & MCL_CURRENT)
- newflags &= ~VM_LOCKED;
+ newflags = vma->vm_flags & ~to_clear;
/* Ignore errors */
mlock_fixup(vma, &prev, vma->vm_start, vma->vm_end, newflags);
@@ -766,7 +804,7 @@ SYSCALL_DEFINE0(munlockall)
int ret;
down_write(¤t->mm->mmap_sem);
- ret = do_munlockall(MCL_CURRENT | MCL_FUTURE);
+ ret = do_munlockall(MCL_CURRENT | MCL_FUTURE | MCL_ONFAULT);
up_write(¤t->mm->mmap_sem);
return ret;
}
@@ -775,7 +813,7 @@ SYSCALL_DEFINE1(munlockall2, int, flags)
{
int ret = -EINVAL;
- if (!flags || flags & ~(MCL_CURRENT | MCL_FUTURE))
+ if (!flags || flags & ~(MCL_CURRENT | MCL_FUTURE | MCL_ONFAULT))
return ret;
down_write(¤t->mm->mmap_sem);
diff --git a/mm/mmap.c b/mm/mmap.c
index aa632ad..de89be4 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1232,8 +1232,8 @@ static inline int mlock_future_check(struct mm_struct *mm,
{
unsigned long locked, lock_limit;
- /* mlock MCL_FUTURE? */
- if (flags & VM_LOCKED) {
+ /* mlock MCL_FUTURE or MCL_ONFAULT? */
+ if (flags & (VM_LOCKED | VM_LOCKONFAULT)) {
locked = len >> PAGE_SHIFT;
locked += mm->locked_vm;
lock_limit = rlimit(RLIMIT_MEMLOCK);
@@ -1646,12 +1646,12 @@ out:
perf_event_mmap(vma);
vm_stat_account(mm, vm_flags, file, len >> PAGE_SHIFT);
- if (vm_flags & VM_LOCKED) {
+ if (vm_flags & (VM_LOCKED | VM_LOCKONFAULT)) {
if (!((vm_flags & VM_SPECIAL) || is_vm_hugetlb_page(vma) ||
vma == get_gate_vma(current->mm)))
mm->locked_vm += (len >> PAGE_SHIFT);
else
- vma->vm_flags &= ~VM_LOCKED;
+ vma->vm_flags &= ~(VM_LOCKED | VM_LOCKONFAULT);
}
if (file)
@@ -2104,7 +2104,7 @@ static int acct_stack_growth(struct vm_area_struct *vma, unsigned long size, uns
return -ENOMEM;
/* mlock limit tests */
- if (vma->vm_flags & VM_LOCKED) {
+ if (vma->vm_flags & (VM_LOCKED | VM_LOCKONFAULT)) {
unsigned long locked;
unsigned long limit;
locked = mm->locked_vm + grow;
@@ -2128,7 +2128,7 @@ static int acct_stack_growth(struct vm_area_struct *vma, unsigned long size, uns
return -ENOMEM;
/* Ok, everything looks good - let it rip */
- if (vma->vm_flags & VM_LOCKED)
+ if (vma->vm_flags & (VM_LOCKED | VM_LOCKONFAULT))
mm->locked_vm += grow;
vm_stat_account(mm, vma->vm_flags, vma->vm_file, grow);
return 0;
@@ -2583,7 +2583,7 @@ int do_munmap(struct mm_struct *mm, unsigned long start, size_t len)
if (mm->locked_vm) {
struct vm_area_struct *tmp = vma;
while (tmp && tmp->vm_start < end) {
- if (tmp->vm_flags & VM_LOCKED) {
+ if (tmp->vm_flags & (VM_LOCKED | VM_LOCKONFAULT)) {
mm->locked_vm -= vma_pages(tmp);
munlock_vma_pages_all(tmp);
}
@@ -2636,6 +2636,7 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size,
unsigned long populate = 0;
unsigned long ret = -EINVAL;
struct file *file;
+ vm_flags_t drop_lock_flag = 0;
pr_warn_once("%s (%d) uses deprecated remap_file_pages() syscall. "
"See Documentation/vm/remap_file_pages.txt.\n",
@@ -2675,10 +2676,15 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size,
flags |= MAP_SHARED | MAP_FIXED | MAP_POPULATE;
if (vma->vm_flags & VM_LOCKED) {
flags |= MAP_LOCKED;
- /* drop PG_Mlocked flag for over-mapped range */
- munlock_vma_pages_range(vma, start, start + size);
+ drop_lock_flag = VM_LOCKED;
+ } else if (vma->vm_flags & VM_LOCKONFAULT) {
+ drop_lock_flag = VM_LOCKONFAULT;
}
+ if (drop_lock_flag)
+ /* drop PG_Mlocked flag for over-mapped range */
+ munlock_vma_pages_range(vma, start, start + size, VM_LOCKED);
+
file = get_file(vma->vm_file);
ret = do_mmap_pgoff(vma->vm_file, start, size,
prot, flags, pgoff, &populate);
@@ -2781,7 +2787,7 @@ static unsigned long do_brk(unsigned long addr, unsigned long len)
out:
perf_event_mmap(vma);
mm->total_vm += len >> PAGE_SHIFT;
- if (flags & VM_LOCKED)
+ if (flags & (VM_LOCKED | VM_LOCKONFAULT))
mm->locked_vm += (len >> PAGE_SHIFT);
vma->vm_flags |= VM_SOFTDIRTY;
return addr;
@@ -2816,7 +2822,7 @@ void exit_mmap(struct mm_struct *mm)
if (mm->locked_vm) {
vma = mm->mmap;
while (vma) {
- if (vma->vm_flags & VM_LOCKED)
+ if (vma->vm_flags & (VM_LOCKED | VM_LOCKONFAULT))
munlock_vma_pages_all(vma);
vma = vma->vm_next;
}
diff --git a/mm/mremap.c b/mm/mremap.c
index a7c93ec..44d4c44 100644
--- a/mm/mremap.c
+++ b/mm/mremap.c
@@ -335,7 +335,7 @@ static unsigned long move_vma(struct vm_area_struct *vma,
vma->vm_next->vm_flags |= VM_ACCOUNT;
}
- if (vm_flags & VM_LOCKED) {
+ if (vm_flags & (VM_LOCKED | VM_LOCKONFAULT)) {
mm->locked_vm += new_len >> PAGE_SHIFT;
*locked = true;
}
@@ -371,7 +371,7 @@ static struct vm_area_struct *vma_to_resize(unsigned long addr,
return ERR_PTR(-EINVAL);
}
- if (vma->vm_flags & VM_LOCKED) {
+ if (vma->vm_flags & (VM_LOCKED | VM_LOCKONFAULT)) {
unsigned long locked, lock_limit;
locked = mm->locked_vm << PAGE_SHIFT;
lock_limit = rlimit(RLIMIT_MEMLOCK);
@@ -548,7 +548,7 @@ SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len,
}
vm_stat_account(mm, vma->vm_flags, vma->vm_file, pages);
- if (vma->vm_flags & VM_LOCKED) {
+ if (vma->vm_flags & (VM_LOCKED | VM_LOCKONFAULT)) {
mm->locked_vm += pages;
locked = true;
new_addr = addr;
diff --git a/mm/msync.c b/mm/msync.c
index bb04d53..1183183 100644
--- a/mm/msync.c
+++ b/mm/msync.c
@@ -73,7 +73,7 @@ SYSCALL_DEFINE3(msync, unsigned long, start, size_t, len, int, flags)
}
/* Here vma->vm_start <= start < vma->vm_end. */
if ((flags & MS_INVALIDATE) &&
- (vma->vm_flags & VM_LOCKED)) {
+ (vma->vm_flags & (VM_LOCKED | VM_LOCKONFAULT))) {
error = -EBUSY;
goto out_unlock;
}
diff --git a/mm/rmap.c b/mm/rmap.c
index 171b687..3e91372 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -742,9 +742,9 @@ static int page_referenced_one(struct page *page, struct vm_area_struct *vma,
if (!pmd)
return SWAP_AGAIN;
- if (vma->vm_flags & VM_LOCKED) {
+ if (vma->vm_flags & (VM_LOCKED | VM_LOCKONFAULT)) {
spin_unlock(ptl);
- pra->vm_flags |= VM_LOCKED;
+ pra->vm_flags |= (vma->vm_flags & (VM_LOCKED | VM_LOCKONFAULT));
return SWAP_FAIL; /* To break the loop */
}
@@ -763,9 +763,9 @@ static int page_referenced_one(struct page *page, struct vm_area_struct *vma,
if (!pte)
return SWAP_AGAIN;
- if (vma->vm_flags & VM_LOCKED) {
+ if (vma->vm_flags & (VM_LOCKED | VM_LOCKONFAULT)) {
pte_unmap_unlock(pte, ptl);
- pra->vm_flags |= VM_LOCKED;
+ pra->vm_flags |= (vma->vm_flags & (VM_LOCKED | VM_LOCKONFAULT));
return SWAP_FAIL; /* To break the loop */
}
@@ -1205,7 +1205,7 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
* skipped over this mm) then we should reactivate it.
*/
if (!(flags & TTU_IGNORE_MLOCK)) {
- if (vma->vm_flags & VM_LOCKED)
+ if (vma->vm_flags & (VM_LOCKED | VM_LOCKONFAULT))
goto out_mlock;
if (flags & TTU_MUNLOCK)
@@ -1315,7 +1315,7 @@ out_mlock:
* page is actually mlocked.
*/
if (down_read_trylock(&vma->vm_mm->mmap_sem)) {
- if (vma->vm_flags & VM_LOCKED) {
+ if (vma->vm_flags & (VM_LOCKED | VM_LOCKONFAULT)) {
mlock_vma_page(page);
ret = SWAP_MLOCK;
}
diff --git a/mm/shmem.c b/mm/shmem.c
index 4caf8ed..9ddf2ca 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -754,7 +754,7 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc)
index = page->index;
inode = mapping->host;
info = SHMEM_I(inode);
- if (info->flags & VM_LOCKED)
+ if (info->flags & (VM_LOCKED | VM_LOCKONFAULT))
goto redirty;
if (!total_swap_pages)
goto redirty;
diff --git a/mm/swap.c b/mm/swap.c
index a3a0a2f..3580a21 100644
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -710,7 +710,8 @@ void lru_cache_add_active_or_unevictable(struct page *page,
{
VM_BUG_ON_PAGE(PageLRU(page), page);
- if (likely((vma->vm_flags & (VM_LOCKED | VM_SPECIAL)) != VM_LOCKED)) {
+ if (likely((vma->vm_flags & (VM_LOCKED | VM_LOCKONFAULT)) == 0) ||
+ (vma->vm_flags & VM_SPECIAL)) {
SetPageActive(page);
lru_cache_add(page);
return;
diff --git a/mm/vmscan.c b/mm/vmscan.c
index e61445d..019d306 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -804,7 +804,7 @@ static enum page_references page_check_references(struct page *page,
* Mlock lost the isolation race with us. Let try_to_unmap()
* move the page to the unevictable list.
*/
- if (vm_flags & VM_LOCKED)
+ if (vm_flags & (VM_LOCKED | VM_LOCKONFAULT))
return PAGEREF_RECLAIM;
if (referenced_ptes) {
--
1.9.1
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply related
* [PATCH V4 5/6] mm: mmap: Add mmap flag to request VM_LOCKONFAULT
From: Eric B Munson @ 2015-07-21 19:59 UTC (permalink / raw)
To: Andrew Morton
Cc: Eric B Munson, Michal Hocko, Vlastimil Babka, Paul Gortmaker,
Chris Metcalf, Guenter Roeck, linux-alpha, linux-kernel,
linux-mips, linux-parisc, linuxppc-dev, sparclinux, linux-xtensa,
linux-mm, linux-arch, linux-api
In-Reply-To: <1437508781-28655-1-git-send-email-emunson@akamai.com>
The cost of faulting in all memory to be locked can be very high when
working with large mappings. If only portions of the mapping will be
used this can incur a high penalty for locking.
Now that we have the new VMA flag for the locked but not present state,
expose it as an mmap option like MAP_LOCKED -> VM_LOCKED.
Signed-off-by: Eric B Munson <emunson@akamai.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Paul Gortmaker <paul.gortmaker@windriver.com>
Cc: Chris Metcalf <cmetcalf@ezchip.com>
Cc: Guenter Roeck <linux@roeck-us.net>
Cc: linux-alpha@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-mips@linux-mips.org
Cc: linux-parisc@vger.kernel.org
Cc: linuxppc-dev@lists.ozlabs.org
Cc: sparclinux@vger.kernel.org
Cc: linux-xtensa@linux-xtensa.org
Cc: linux-mm@kvack.org
Cc: linux-arch@vger.kernel.org
Cc: linux-api@vger.kernel.org
---
Changes from V3:
Add missing MAP_LOCKONFAULT to tile
arch/alpha/include/uapi/asm/mman.h | 1 +
arch/mips/include/uapi/asm/mman.h | 1 +
arch/parisc/include/uapi/asm/mman.h | 1 +
arch/powerpc/include/uapi/asm/mman.h | 1 +
arch/sparc/include/uapi/asm/mman.h | 1 +
arch/tile/include/uapi/asm/mman.h | 1 +
arch/xtensa/include/uapi/asm/mman.h | 1 +
include/linux/mman.h | 3 ++-
include/uapi/asm-generic/mman.h | 1 +
kernel/events/core.c | 2 ++
mm/mmap.c | 6 ++++--
11 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/arch/alpha/include/uapi/asm/mman.h b/arch/alpha/include/uapi/asm/mman.h
index 77ae8db..3f80ca4 100644
--- a/arch/alpha/include/uapi/asm/mman.h
+++ b/arch/alpha/include/uapi/asm/mman.h
@@ -30,6 +30,7 @@
#define MAP_NONBLOCK 0x40000 /* do not block on IO */
#define MAP_STACK 0x80000 /* give out an address that is best suited for process/thread stacks */
#define MAP_HUGETLB 0x100000 /* create a huge page mapping */
+#define MAP_LOCKONFAULT 0x200000 /* Lock pages after they are faulted in, do not prefault */
#define MS_ASYNC 1 /* sync memory asynchronously */
#define MS_SYNC 2 /* synchronous memory sync */
diff --git a/arch/mips/include/uapi/asm/mman.h b/arch/mips/include/uapi/asm/mman.h
index 71ed81d..905c1ea 100644
--- a/arch/mips/include/uapi/asm/mman.h
+++ b/arch/mips/include/uapi/asm/mman.h
@@ -48,6 +48,7 @@
#define MAP_NONBLOCK 0x20000 /* do not block on IO */
#define MAP_STACK 0x40000 /* give out an address that is best suited for process/thread stacks */
#define MAP_HUGETLB 0x80000 /* create a huge page mapping */
+#define MAP_LOCKONFAULT 0x100000 /* Lock pages after they are faulted in, do not prefault */
/*
* Flags for msync
diff --git a/arch/parisc/include/uapi/asm/mman.h b/arch/parisc/include/uapi/asm/mman.h
index c0871ce..c4695f6 100644
--- a/arch/parisc/include/uapi/asm/mman.h
+++ b/arch/parisc/include/uapi/asm/mman.h
@@ -24,6 +24,7 @@
#define MAP_NONBLOCK 0x20000 /* do not block on IO */
#define MAP_STACK 0x40000 /* give out an address that is best suited for process/thread stacks */
#define MAP_HUGETLB 0x80000 /* create a huge page mapping */
+#define MAP_LOCKONFAULT 0x100000 /* Lock pages after they are faulted in, do not prefault */
#define MS_SYNC 1 /* synchronous memory sync */
#define MS_ASYNC 2 /* sync memory asynchronously */
diff --git a/arch/powerpc/include/uapi/asm/mman.h b/arch/powerpc/include/uapi/asm/mman.h
index f93f7eb..40a3fda 100644
--- a/arch/powerpc/include/uapi/asm/mman.h
+++ b/arch/powerpc/include/uapi/asm/mman.h
@@ -31,5 +31,6 @@
#define MAP_NONBLOCK 0x10000 /* do not block on IO */
#define MAP_STACK 0x20000 /* give out an address that is best suited for process/thread stacks */
#define MAP_HUGETLB 0x40000 /* create a huge page mapping */
+#define MAP_LOCKONFAULT 0x80000 /* Lock pages after they are faulted in, do not prefault */
#endif /* _UAPI_ASM_POWERPC_MMAN_H */
diff --git a/arch/sparc/include/uapi/asm/mman.h b/arch/sparc/include/uapi/asm/mman.h
index 8cd2ebc..3d74ab7 100644
--- a/arch/sparc/include/uapi/asm/mman.h
+++ b/arch/sparc/include/uapi/asm/mman.h
@@ -26,6 +26,7 @@
#define MAP_NONBLOCK 0x10000 /* do not block on IO */
#define MAP_STACK 0x20000 /* give out an address that is best suited for process/thread stacks */
#define MAP_HUGETLB 0x40000 /* create a huge page mapping */
+#define MAP_LOCKONFAULT 0x8000 /* Lock pages after they are faulted in, do not prefault */
#endif /* _UAPI__SPARC_MMAN_H__ */
diff --git a/arch/tile/include/uapi/asm/mman.h b/arch/tile/include/uapi/asm/mman.h
index acdd013..800e5c3 100644
--- a/arch/tile/include/uapi/asm/mman.h
+++ b/arch/tile/include/uapi/asm/mman.h
@@ -29,6 +29,7 @@
#define MAP_DENYWRITE 0x0800 /* ETXTBSY */
#define MAP_EXECUTABLE 0x1000 /* mark it as an executable */
#define MAP_HUGETLB 0x4000 /* create a huge page mapping */
+#define MAP_LOCKONFAULT 0x100000 /* Lock pages after they are faulted in, do not prefault */
/*
diff --git a/arch/xtensa/include/uapi/asm/mman.h b/arch/xtensa/include/uapi/asm/mman.h
index 5725a15..689e1f2 100644
--- a/arch/xtensa/include/uapi/asm/mman.h
+++ b/arch/xtensa/include/uapi/asm/mman.h
@@ -55,6 +55,7 @@
#define MAP_NONBLOCK 0x20000 /* do not block on IO */
#define MAP_STACK 0x40000 /* give out an address that is best suited for process/thread stacks */
#define MAP_HUGETLB 0x80000 /* create a huge page mapping */
+#define MAP_LOCKONFAULT 0x100000 /* Lock pages after they are faulted in, do not prefault */
#ifdef CONFIG_MMAP_ALLOW_UNINITIALIZED
# define MAP_UNINITIALIZED 0x4000000 /* For anonymous mmap, memory could be
* uninitialized */
diff --git a/include/linux/mman.h b/include/linux/mman.h
index 16373c8..437264b 100644
--- a/include/linux/mman.h
+++ b/include/linux/mman.h
@@ -86,7 +86,8 @@ calc_vm_flag_bits(unsigned long flags)
{
return _calc_vm_trans(flags, MAP_GROWSDOWN, VM_GROWSDOWN ) |
_calc_vm_trans(flags, MAP_DENYWRITE, VM_DENYWRITE ) |
- _calc_vm_trans(flags, MAP_LOCKED, VM_LOCKED );
+ _calc_vm_trans(flags, MAP_LOCKED, VM_LOCKED ) |
+ _calc_vm_trans(flags, MAP_LOCKONFAULT,VM_LOCKONFAULT);
}
unsigned long vm_commit_limit(void);
diff --git a/include/uapi/asm-generic/mman.h b/include/uapi/asm-generic/mman.h
index 555aab0..007b784 100644
--- a/include/uapi/asm-generic/mman.h
+++ b/include/uapi/asm-generic/mman.h
@@ -12,6 +12,7 @@
#define MAP_NONBLOCK 0x10000 /* do not block on IO */
#define MAP_STACK 0x20000 /* give out an address that is best suited for process/thread stacks */
#define MAP_HUGETLB 0x40000 /* create a huge page mapping */
+#define MAP_LOCKONFAULT 0x80000 /* Lock pages after they are faulted in, do not prefault */
/* Bits [26:31] are reserved, see mman-common.h for MAP_HUGETLB usage */
diff --git a/kernel/events/core.c b/kernel/events/core.c
index d3dae34..53f312c 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -5816,6 +5816,8 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event)
flags |= MAP_EXECUTABLE;
if (vma->vm_flags & VM_LOCKED)
flags |= MAP_LOCKED;
+ if (vma->vm_flags & VM_LOCKONFAULT)
+ flags |= MAP_LOCKONFAULT;
if (vma->vm_flags & VM_HUGETLB)
flags |= MAP_HUGETLB;
diff --git a/mm/mmap.c b/mm/mmap.c
index de89be4..54715b6 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1301,7 +1301,7 @@ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
vm_flags = calc_vm_prot_bits(prot) | calc_vm_flag_bits(flags) |
mm->def_flags | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
- if (flags & MAP_LOCKED)
+ if (flags & (MAP_LOCKED | MAP_LOCKONFAULT))
if (!can_do_mlock())
return -EPERM;
@@ -2678,12 +2678,14 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size,
flags |= MAP_LOCKED;
drop_lock_flag = VM_LOCKED;
} else if (vma->vm_flags & VM_LOCKONFAULT) {
+ flags |= MAP_LOCKONFAULT;
drop_lock_flag = VM_LOCKONFAULT;
}
+
if (drop_lock_flag)
/* drop PG_Mlocked flag for over-mapped range */
- munlock_vma_pages_range(vma, start, start + size, VM_LOCKED);
+ munlock_vma_pages_range(vma, start, start + size, drop_lock_flag);
file = get_file(vma->vm_file);
ret = do_mmap_pgoff(vma->vm_file, start, size,
--
1.9.1
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply related
* [PATCH V4 6/6] selftests: vm: Add tests for lock on fault
From: Eric B Munson @ 2015-07-21 19:59 UTC (permalink / raw)
To: Andrew Morton
Cc: Eric B Munson, Shuah Khan, Michal Hocko, Vlastimil Babka,
Jonathan Corbet, linux-mm, linux-kernel, linux-api
In-Reply-To: <1437508781-28655-1-git-send-email-emunson@akamai.com>
Test the mmap() flag, and the mlockall() flag. These tests ensure that
pages are not faulted in until they are accessed, that the pages are
unevictable once faulted in, and that VMA splitting and merging works
with the new VM flag. The second test ensures that mlock limits are
respected. Note that the limit test needs to be run a normal user.
Also add tests to use the new mlock2 family of system calls.
Signed-off-by: Eric B Munson <emunson@akamai.com>
Cc: Shuah Khan <shuahkh@osg.samsung.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: linux-mm@kvack.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-api@vger.kernel.org
---
Changes from V3:
Add tests for new mlock2 family of system calls
tools/testing/selftests/vm/Makefile | 3 +
tools/testing/selftests/vm/lock-on-fault.c | 344 ++++++++++++++++
tools/testing/selftests/vm/mlock2-tests.c | 617 ++++++++++++++++++++++++++++
tools/testing/selftests/vm/on-fault-limit.c | 47 +++
tools/testing/selftests/vm/run_vmtests | 33 ++
5 files changed, 1044 insertions(+)
create mode 100644 tools/testing/selftests/vm/lock-on-fault.c
create mode 100644 tools/testing/selftests/vm/mlock2-tests.c
create mode 100644 tools/testing/selftests/vm/on-fault-limit.c
diff --git a/tools/testing/selftests/vm/Makefile b/tools/testing/selftests/vm/Makefile
index 231b9a0..0fe6524 100644
--- a/tools/testing/selftests/vm/Makefile
+++ b/tools/testing/selftests/vm/Makefile
@@ -5,7 +5,10 @@ BINARIES = compaction_test
BINARIES += hugepage-mmap
BINARIES += hugepage-shm
BINARIES += hugetlbfstest
+BINARIES += lock-on-fault
BINARIES += map_hugetlb
+BINARIES += mlock2-tests
+BINARIES += on-fault-limit
BINARIES += thuge-gen
BINARIES += transhuge-stress
diff --git a/tools/testing/selftests/vm/lock-on-fault.c b/tools/testing/selftests/vm/lock-on-fault.c
new file mode 100644
index 0000000..f02c9fb
--- /dev/null
+++ b/tools/testing/selftests/vm/lock-on-fault.c
@@ -0,0 +1,344 @@
+#include <sys/mman.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <errno.h>
+
+struct vm_boundaries {
+ unsigned long start;
+ unsigned long end;
+};
+
+static int get_vm_area(unsigned long addr, struct vm_boundaries *area)
+{
+ FILE *file;
+ int ret = 1;
+ char line[1024] = {0};
+ char *end_addr;
+ char *stop;
+ unsigned long start;
+ unsigned long end;
+
+ if (!area)
+ return ret;
+
+ file = fopen("/proc/self/maps", "r");
+ if (!file) {
+ perror("fopen");
+ return ret;
+ }
+
+ memset(area, 0, sizeof(struct vm_boundaries));
+
+ while(fgets(line, 1024, file)) {
+ end_addr = strchr(line, '-');
+ if (!end_addr) {
+ printf("cannot parse /proc/self/maps\n");
+ goto out;
+ }
+ *end_addr = '\0';
+ end_addr++;
+ stop = strchr(end_addr, ' ');
+ if (!stop) {
+ printf("cannot parse /proc/self/maps\n");
+ goto out;
+ }
+ stop = '\0';
+
+ sscanf(line, "%lx", &start);
+ sscanf(end_addr, "%lx", &end);
+
+ if (start <= addr && end > addr) {
+ area->start = start;
+ area->end = end;
+ ret = 0;
+ goto out;
+ }
+ }
+out:
+ fclose(file);
+ return ret;
+}
+
+static unsigned long get_pageflags(unsigned long addr)
+{
+ FILE *file;
+ unsigned long pfn;
+ unsigned long offset;
+
+ file = fopen("/proc/self/pagemap", "r");
+ if (!file) {
+ perror("fopen");
+ _exit(1);
+ }
+
+ offset = addr / getpagesize() * sizeof(unsigned long);
+ if (fseek(file, offset, SEEK_SET)) {
+ perror("fseek");
+ _exit(1);
+ }
+
+ if (fread(&pfn, sizeof(unsigned long), 1, file) != 1) {
+ perror("fread");
+ _exit(1);
+ }
+
+ fclose(file);
+ return pfn;
+}
+
+static unsigned long get_kpageflags(unsigned long pfn)
+{
+ unsigned long flags;
+ FILE *file;
+
+ file = fopen("/proc/kpageflags", "r");
+ if (!file) {
+ perror("fopen");
+ _exit(1);
+ }
+
+ if (fseek(file, pfn * sizeof(unsigned long), SEEK_SET)) {
+ perror("fseek");
+ _exit(1);
+ }
+
+ if (fread(&flags, sizeof(unsigned long), 1, file) != 1) {
+ perror("fread");
+ _exit(1);
+ }
+
+ fclose(file);
+ return flags;
+}
+
+#define PRESENT_BIT 0x8000000000000000
+#define PFN_MASK 0x007FFFFFFFFFFFFF
+#define UNEVICTABLE_BIT (1UL << 18)
+
+static int test_mmap(int flags)
+{
+ unsigned long page1_flags;
+ unsigned long page2_flags;
+ void *map;
+ unsigned long page_size = getpagesize();
+
+ map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE, flags, 0, 0);
+ if (map == MAP_FAILED) {
+ perror("mmap()");
+ return 1;
+ }
+
+ /* Write something into the first page to ensure it is present */
+ *(char *)map = 1;
+
+ page1_flags = get_pageflags((unsigned long)map);
+ page2_flags = get_pageflags((unsigned long)map + page_size);
+
+ /* page2_flags should not be present */
+ if (page2_flags & PRESENT_BIT) {
+ printf("page map says 0x%lx\n", page2_flags);
+ printf("present is 0x%lx\n", PRESENT_BIT);
+ return 1;
+ }
+
+ /* page1_flags should be present */
+ if ((page1_flags & PRESENT_BIT) == 0) {
+ printf("page map says 0x%lx\n", page1_flags);
+ printf("present is 0x%lx\n", PRESENT_BIT);
+ return 1;
+ }
+
+ page1_flags = get_kpageflags(page1_flags & PFN_MASK);
+
+ /* page1_flags now contains the entry from kpageflags for the first
+ * page, the unevictable bit should be set */
+ if ((page1_flags & UNEVICTABLE_BIT) == 0) {
+ printf("kpageflags says 0x%lx\n", page1_flags);
+ printf("unevictable is 0x%lx\n", UNEVICTABLE_BIT);
+ return 1;
+ }
+
+ munmap(map, 2 * page_size);
+ return 0;
+}
+
+static int test_munlock(int flags)
+{
+ int ret = 1;
+ void *map;
+ unsigned long page1_flags;
+ unsigned long page2_flags;
+ unsigned long page3_flags;
+ unsigned long page_size = getpagesize();
+
+ map = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE, flags, 0, 0);
+ if (map == MAP_FAILED) {
+ perror("mmap()");
+ return ret;
+ }
+
+ if (munlock(map + page_size, page_size)) {
+ perror("munlock()");
+ goto out;
+ }
+
+ page1_flags = get_pageflags((unsigned long)map);
+ page2_flags = get_pageflags((unsigned long)map + page_size);
+ page3_flags = get_pageflags((unsigned long)map + page_size * 2);
+
+ /* No pages should be present */
+ if ((page1_flags & PRESENT_BIT) || (page2_flags & PRESENT_BIT) ||
+ (page3_flags & PRESENT_BIT)) {
+ printf("Page was made present by munlock()\n");
+ goto out;
+ }
+
+ /* Write something to each page so that they are faulted in */
+ *(char*)map = 1;
+ *(char*)(map + page_size) = 1;
+ *(char*)(map + page_size * 2) = 1;
+
+ page1_flags = get_pageflags((unsigned long)map);
+ page2_flags = get_pageflags((unsigned long)map + page_size);
+ page3_flags = get_pageflags((unsigned long)map + page_size * 2);
+
+ page1_flags = get_kpageflags(page1_flags & PFN_MASK);
+ page2_flags = get_kpageflags(page2_flags & PFN_MASK);
+ page3_flags = get_kpageflags(page3_flags & PFN_MASK);
+
+ /* Pages 1 and 3 should be unevictable */
+ if (!(page1_flags & UNEVICTABLE_BIT)) {
+ printf("Missing unevictable bit on lock on fault page1\n");
+ goto out;
+ }
+ if (!(page3_flags & UNEVICTABLE_BIT)) {
+ printf("Missing unevictable bit on lock on fault page3\n");
+ goto out;
+ }
+
+ /* Page 2 should not be unevictable */
+ if (page2_flags & UNEVICTABLE_BIT) {
+ printf("Unlocked page is still marked unevictable\n");
+ goto out;
+ }
+
+ ret = 0;
+
+out:
+ munmap(map, 3 * page_size);
+ return ret;
+}
+
+static int test_vma_management(int flags)
+{
+ int ret = 1;
+ void *map;
+ unsigned long page_size = getpagesize();
+ struct vm_boundaries page1;
+ struct vm_boundaries page2;
+ struct vm_boundaries page3;
+
+ map = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE, flags, 0, 0);
+ if (map == MAP_FAILED) {
+ perror("mmap()");
+ return ret;
+ }
+
+ if (get_vm_area((unsigned long)map, &page1) ||
+ get_vm_area((unsigned long)map + page_size, &page2) ||
+ get_vm_area((unsigned long)map + page_size * 2, &page3)) {
+ printf("couldn't find mapping in /proc/self/maps\n");
+ goto out;
+ }
+
+ /*
+ * Before we unlock a portion, we need to that all three pages are in
+ * the same VMA. If they are not we abort this test (Note that this is
+ * not a failure)
+ */
+ if (page1.start != page2.start || page2.start != page3.start) {
+ printf("VMAs are not merged to start, aborting test\n");
+ ret = 0;
+ goto out;
+ }
+
+ if (munlock(map + page_size, page_size)) {
+ perror("munlock()");
+ goto out;
+ }
+
+ if (get_vm_area((unsigned long)map, &page1) ||
+ get_vm_area((unsigned long)map + page_size, &page2) ||
+ get_vm_area((unsigned long)map + page_size * 2, &page3)) {
+ printf("couldn't find mapping in /proc/self/maps\n");
+ goto out;
+ }
+
+ /* All three VMAs should be different */
+ if (page1.start == page2.start || page2.start == page3.start) {
+ printf("failed to split VMA for munlock\n");
+ goto out;
+ }
+
+ /* Now unlock the first and third page and check the VMAs again */
+ if (munlock(map, page_size * 3)) {
+ perror("munlock()");
+ goto out;
+ }
+
+ if (get_vm_area((unsigned long)map, &page1) ||
+ get_vm_area((unsigned long)map + page_size, &page2) ||
+ get_vm_area((unsigned long)map + page_size * 2, &page3)) {
+ printf("couldn't find mapping in /proc/self/maps\n");
+ goto out;
+ }
+
+ /* Now all three VMAs should be the same */
+ if (page1.start != page2.start || page2.start != page3.start) {
+ printf("failed to merge VMAs after munlock\n");
+ goto out;
+ }
+
+ ret = 0;
+out:
+ munmap(map, 3 * page_size);
+ return ret;
+}
+
+#ifndef MCL_ONFAULT
+#define MCL_ONFAULT (MCL_FUTURE << 1)
+#endif
+
+static int test_mlockall(int (test_function)(int flags))
+{
+ int ret = 1;
+
+ if (mlockall(MCL_ONFAULT)) {
+ perror("mlockall");
+ return ret;
+ }
+
+ ret = test_function(MAP_PRIVATE | MAP_ANONYMOUS);
+ munlockall();
+ return ret;
+}
+
+#ifndef MAP_LOCKONFAULT
+#define MAP_LOCKONFAULT (MAP_HUGETLB << 1)
+#endif
+
+int main(int argc, char **argv)
+{
+ int ret = 0;
+ ret += test_mmap(MAP_PRIVATE | MAP_ANONYMOUS | MAP_LOCKONFAULT);
+ ret += test_mlockall(test_mmap);
+ ret += test_munlock(MAP_PRIVATE | MAP_ANONYMOUS | MAP_LOCKONFAULT);
+ ret += test_mlockall(test_munlock);
+ ret += test_vma_management(MAP_PRIVATE | MAP_ANONYMOUS | MAP_LOCKONFAULT);
+ ret += test_mlockall(test_vma_management);
+ return ret;
+}
+
diff --git a/tools/testing/selftests/vm/mlock2-tests.c b/tools/testing/selftests/vm/mlock2-tests.c
new file mode 100644
index 0000000..22ab749
--- /dev/null
+++ b/tools/testing/selftests/vm/mlock2-tests.c
@@ -0,0 +1,617 @@
+#include <sys/mman.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <errno.h>
+#include <stdbool.h>
+
+#ifndef MLOCK_LOCK
+#define MLOCK_LOCK 1
+#endif
+
+#ifndef MLOCK_ONFAULT
+#define MLOCK_ONFAULT 2
+#endif
+
+#ifndef MCL_ONFAULT
+#define MCL_ONFAULT (MCL_FUTURE << 1)
+#endif
+
+static int mlock2_(void *start, size_t len, int flags)
+{
+#ifdef __NR_mlock2
+ return syscall(__NR_mlock2, start, len, flags);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+static int munlock2_(void *start, size_t len, int flags)
+{
+#ifdef __NR_munlock2
+ return syscall(__NR_munlock2, start, len, flags);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+static int munlockall2_(int flags)
+{
+#ifdef __NR_munlockall2
+ return syscall(__NR_munlockall2, flags);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+static unsigned long get_pageflags(unsigned long addr)
+{
+ FILE *file;
+ unsigned long pfn;
+ unsigned long offset;
+
+ file = fopen("/proc/self/pagemap", "r");
+ if (!file) {
+ perror("fopen pagemap");
+ _exit(1);
+ }
+
+ offset = addr / getpagesize() * sizeof(unsigned long);
+ if (fseek(file, offset, SEEK_SET)) {
+ perror("fseek pagemap");
+ _exit(1);
+ }
+
+ if (fread(&pfn, sizeof(unsigned long), 1, file) != 1) {
+ perror("fread pagemap");
+ _exit(1);
+ }
+
+ fclose(file);
+ return pfn;
+}
+
+static unsigned long get_kpageflags(unsigned long pfn)
+{
+ unsigned long flags;
+ FILE *file;
+
+ file = fopen("/proc/kpageflags", "r");
+ if (!file) {
+ perror("fopen kpageflags");
+ _exit(1);
+ }
+
+ if (fseek(file, pfn * sizeof(unsigned long), SEEK_SET)) {
+ perror("fseek kpageflags");
+ _exit(1);
+ }
+
+ if (fread(&flags, sizeof(unsigned long), 1, file) != 1) {
+ perror("fread kpageflags");
+ _exit(1);
+ }
+
+ fclose(file);
+ return flags;
+}
+
+#define VMFLAGS "VmFlags:"
+
+static bool find_flag(FILE *file, const char *vmflag)
+{
+ char *line = NULL;
+ char *flags;
+ size_t size = 0;
+ bool ret = false;
+
+ while (getline(&line, &size, file) > 0) {
+ if (!strstr(line, VMFLAGS)) {
+ free(line);
+ line = NULL;
+ size = 0;
+ continue;
+ }
+
+ flags = line + strlen(VMFLAGS);
+ ret = (strstr(flags, vmflag) != NULL);
+ goto out;
+ }
+
+out:
+ free(line);
+ return ret;
+}
+
+static bool is_vmflag_set(unsigned long addr, const char *vmflag)
+{
+ FILE *file;
+ char *line = NULL;
+ size_t size = 0;
+ bool ret = false;
+ unsigned long start, end;
+ char perms[5];
+ unsigned long offset;
+ char dev[32];
+ unsigned long inode;
+ char path[BUFSIZ];
+
+ file = fopen("/proc/self/smaps", "r");
+ if (!file) {
+ perror("fopen smaps");
+ _exit(1);
+ }
+
+ while (getline(&line, &size, file) > 0) {
+ if (sscanf(line, "%lx-%lx %s %lx %s %lu %s\n",
+ &start, &end, perms, &offset, dev, &inode, path) < 6)
+ goto next;
+
+ if (start <= addr && addr < end) {
+ ret = find_flag(file, vmflag);
+ goto out;
+ }
+
+next:
+ free(line);
+ line = NULL;
+ size = 0;
+ }
+
+out:
+ free(line);
+ fclose(file);
+ return ret;
+}
+
+#define PRESENT_BIT 0x8000000000000000
+#define PFN_MASK 0x007FFFFFFFFFFFFF
+#define UNEVICTABLE_BIT (1UL << 18)
+
+#define LOCKED "lo"
+#define LOCKEDONFAULT "lf"
+
+static int lock_check(char *map)
+{
+ unsigned long page1_flags;
+ unsigned long page2_flags;
+ unsigned long page_size = getpagesize();
+
+ page1_flags = get_pageflags((unsigned long)map);
+ page2_flags = get_pageflags((unsigned long)map + page_size);
+
+ /* Both pages should be present */
+ if (((page1_flags & PRESENT_BIT) == 0) ||
+ ((page2_flags & PRESENT_BIT) == 0)) {
+ printf("Failed to make both pages present\n");
+ return 1;
+ }
+
+ page1_flags = get_kpageflags(page1_flags & PFN_MASK);
+ page2_flags = get_kpageflags(page2_flags & PFN_MASK);
+
+ /* Both pages should be unevictable */
+ if (((page1_flags & UNEVICTABLE_BIT) == 0) ||
+ ((page2_flags & UNEVICTABLE_BIT) == 0)) {
+ printf("Failed to make both pages unevictable\n");
+ return 1;
+ }
+
+ if (!is_vmflag_set((unsigned long)map, LOCKED) ||
+ !is_vmflag_set((unsigned long)map + page_size, LOCKED)) {
+ printf("VMA flag %s is missing\n", LOCKED);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int unlock_lock_check(char *map)
+{
+ unsigned long page1_flags;
+ unsigned long page2_flags;
+ unsigned long page_size = getpagesize();
+
+ page1_flags = get_pageflags((unsigned long)map);
+ page2_flags = get_pageflags((unsigned long)map + page_size);
+ page1_flags = get_kpageflags(page1_flags & PFN_MASK);
+ page2_flags = get_kpageflags(page2_flags & PFN_MASK);
+
+ if ((page1_flags & UNEVICTABLE_BIT) || (page2_flags & UNEVICTABLE_BIT)) {
+ printf("A page is still marked unevictable after unlock\n");
+ return 1;
+ }
+
+ if (is_vmflag_set((unsigned long)map, LOCKED) ||
+ is_vmflag_set((unsigned long)map + page_size, LOCKED)) {
+ printf("VMA flag %s is still set after unlock\n", LOCKED);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int test_mlock_lock()
+{
+ char *map;
+ int ret = 1;
+ unsigned long page_size = getpagesize();
+
+ map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
+ if (map == MAP_FAILED) {
+ perror("test_mlock_locked mmap");
+ goto out;
+ }
+
+ if (mlock2_(map, 2 * page_size, MLOCK_LOCK)) {
+ if (errno == ENOSYS) {
+ printf("Cannot call new mlock family, skipping test\n");
+ _exit(0);
+ }
+ perror("mlock2(MLOCK_LOCK)");
+ goto unmap;
+ }
+
+ if (lock_check(map))
+ goto unmap;
+
+ /* Now clear the MLOCK_LOCK flag and recheck attributes */
+ if (munlock2_(map, 2 * page_size, MLOCK_LOCK)) {
+ if (errno == ENOSYS) {
+ printf("Cannot call new mlock family, skipping test\n");
+ _exit(0);
+ }
+ perror("munlock2(MLOCK_LOCK)");
+ goto unmap;
+ }
+
+ ret = unlock_lock_check(map);
+
+unmap:
+ munmap(map, 2 * page_size);
+out:
+ return ret;
+}
+
+static int onfault_check(char *map)
+{
+ unsigned long page1_flags;
+ unsigned long page2_flags;
+ unsigned long page_size = getpagesize();
+
+ page1_flags = get_pageflags((unsigned long)map);
+ page2_flags = get_pageflags((unsigned long)map + page_size);
+
+ /* Neither page should be present */
+ if ((page1_flags & PRESENT_BIT) || (page2_flags & PRESENT_BIT)) {
+ printf("Pages were made present by MLOCK_ONFAULT\n");
+ return 1;
+ }
+
+ *map = 'a';
+ page1_flags = get_pageflags((unsigned long)map);
+ page2_flags = get_pageflags((unsigned long)map + page_size);
+
+ /* Only page 1 should be present */
+ if ((page1_flags & PRESENT_BIT) == 0) {
+ printf("Page 1 is not present after fault\n");
+ return 1;
+ } else if (page2_flags & PRESENT_BIT) {
+ printf("Page 2 was made present\n");
+ return 1;
+ }
+
+ page1_flags = get_kpageflags(page1_flags & PFN_MASK);
+
+ /* Page 1 should be unevictable */
+ if ((page1_flags & UNEVICTABLE_BIT) == 0) {
+ printf("Failed to make faulted page unevictable\n");
+ return 1;
+ }
+
+ if (!is_vmflag_set((unsigned long)map, LOCKEDONFAULT) ||
+ !is_vmflag_set((unsigned long)map + page_size, LOCKEDONFAULT)) {
+ printf("VMA flag %s is missing\n", LOCKEDONFAULT);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int unlock_onfault_check(char *map)
+{
+ unsigned long page1_flags;
+ unsigned long page2_flags;
+ unsigned long page_size = getpagesize();
+
+ page1_flags = get_pageflags((unsigned long)map);
+ page1_flags = get_kpageflags(page1_flags & PFN_MASK);
+
+ if (page1_flags & UNEVICTABLE_BIT) {
+ printf("Page 1 is still marked unevictable after unlock\n");
+ return 1;
+ }
+
+ if (is_vmflag_set((unsigned long)map, LOCKEDONFAULT) ||
+ is_vmflag_set((unsigned long)map + page_size, LOCKEDONFAULT)) {
+ printf("VMA flag %s is still set after unlock\n", LOCKEDONFAULT);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int test_mlock_onfault()
+{
+ char *map;
+ int ret = 1;
+ unsigned long page_size = getpagesize();
+
+ map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
+ if (map == MAP_FAILED) {
+ perror("test_mlock_locked mmap");
+ goto out;
+ }
+
+ if (mlock2_(map, 2 * page_size, MLOCK_ONFAULT)) {
+ if (errno == ENOSYS) {
+ printf("Cannot call new mlock family, skipping test\n");
+ _exit(0);
+ }
+ perror("mlock2(MLOCK_ONFAULT)");
+ goto unmap;
+ }
+
+ if (onfault_check(map))
+ goto unmap;
+
+ /* Now clear the MLOCK_ONFAULT flag and recheck attributes */
+ if (munlock2_(map, 2 * page_size, MLOCK_ONFAULT)) {
+ if (errno == ENOSYS) {
+ printf("Cannot call new mlock family, skipping test\n");
+ _exit(0);
+ }
+ perror("munlock2(MLOCK_LOCK)");
+ goto unmap;
+ }
+
+ ret = unlock_onfault_check(map);
+unmap:
+ munmap(map, 2 * page_size);
+out:
+ return ret;
+}
+
+static int test_lock_onfault_of_present()
+{
+ char *map;
+ int ret = 1;
+ unsigned long page1_flags;
+ unsigned long page2_flags;
+ unsigned long page_size = getpagesize();
+
+ map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
+ if (map == MAP_FAILED) {
+ perror("test_mlock_locked mmap");
+ goto out;
+ }
+
+ *map = 'a';
+
+ if (mlock2_(map, 2 * page_size, MLOCK_ONFAULT)) {
+ if (errno == ENOSYS) {
+ printf("Cannot call new mlock family, skipping test\n");
+ _exit(0);
+ }
+ perror("mlock2(MLOCK_ONFAULT)");
+ goto unmap;
+ }
+
+ page1_flags = get_pageflags((unsigned long)map);
+ page2_flags = get_pageflags((unsigned long)map + page_size);
+ page1_flags = get_kpageflags(page1_flags & PFN_MASK);
+ page2_flags = get_kpageflags(page2_flags & PFN_MASK);
+
+ /* Page 1 should be unevictable */
+ if ((page1_flags & UNEVICTABLE_BIT) == 0) {
+ printf("Failed to make present page unevictable\n");
+ goto unmap;
+ }
+
+ if (!is_vmflag_set((unsigned long)map, LOCKEDONFAULT) ||
+ !is_vmflag_set((unsigned long)map + page_size, LOCKEDONFAULT)) {
+ printf("VMA flag %s is missing for one of the pages\n", LOCKEDONFAULT);
+ goto unmap;
+ }
+ ret = 0;
+unmap:
+ munmap(map, 2 * page_size);
+out:
+ return ret;
+}
+
+static int test_munlock_mismatch()
+{
+ char *map;
+ int ret = 1;
+ unsigned long page1_flags;
+ unsigned long page2_flags;
+ unsigned long page_size = getpagesize();
+
+ map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
+ if (map == MAP_FAILED) {
+ perror("test_mlock_locked mmap");
+ goto out;
+ }
+
+ if (mlock2_(map, 2 * page_size, MLOCK_LOCK)) {
+ if (errno == ENOSYS) {
+ printf("Cannot call new mlock family, skipping test\n");
+ _exit(0);
+ }
+ perror("mlock2(MLOCK_LOCK)");
+ goto unmap;
+ }
+
+ page1_flags = get_pageflags((unsigned long)map);
+ page2_flags = get_pageflags((unsigned long)map + page_size);
+
+ /* Both pages should be present */
+ if (((page1_flags & PRESENT_BIT) == 0) ||
+ ((page2_flags & PRESENT_BIT) == 0)) {
+ printf("Failed to make both pages present\n");
+ goto unmap;
+ }
+
+ page1_flags = get_kpageflags(page1_flags & PFN_MASK);
+ page2_flags = get_kpageflags(page2_flags & PFN_MASK);
+
+ /* Both pages should be unevictable */
+ if (((page1_flags & UNEVICTABLE_BIT) == 0) ||
+ ((page2_flags & UNEVICTABLE_BIT) == 0)) {
+ printf("Failed to make both pages unevictable\n");
+ goto unmap;
+ }
+
+ if (!is_vmflag_set((unsigned long)map, LOCKED) ||
+ !is_vmflag_set((unsigned long)map + page_size, LOCKED)) {
+ printf("VMA flag %s is missing\n", LOCKED);
+ goto unmap;
+ }
+
+ /* Now clear the MLOCK_ONFAULT flag and recheck attributes */
+ if (munlock2_(map, 2 * page_size, MLOCK_ONFAULT)) {
+ if (errno == ENOSYS) {
+ printf("Cannot call new mlock family, skipping test\n");
+ _exit(0);
+ }
+ perror("munlock2(MLOCK_ONFAULT)");
+ goto unmap;
+ }
+
+ page1_flags = get_pageflags((unsigned long)map);
+ page2_flags = get_pageflags((unsigned long)map + page_size);
+ page1_flags = get_kpageflags(page1_flags & PFN_MASK);
+ page2_flags = get_kpageflags(page2_flags & PFN_MASK);
+
+ if ((page1_flags & UNEVICTABLE_BIT) == 0 ||
+ (page2_flags & UNEVICTABLE_BIT) == 0) {
+ printf("Both pages should still be unevictable but are not\n");
+ goto unmap;
+ }
+
+ if (!is_vmflag_set((unsigned long)map, LOCKED) ||
+ !is_vmflag_set((unsigned long)map + page_size, LOCKED)) {
+ printf("VMA flag %s is not set set after unlock\n", LOCKED);
+ goto unmap;
+ }
+
+ ret = 0;
+unmap:
+ munmap(map, 2 * page_size);
+out:
+ return ret;
+
+}
+
+static int test_munlockall()
+{
+ char *map;
+ int ret = 1;
+ unsigned long page1_flags;
+ unsigned long page2_flags;
+ unsigned long page_size = getpagesize();
+
+ map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
+
+ if (map == MAP_FAILED) {
+ perror("test_munlockall mmap");
+ goto out;
+ }
+
+ if (mlockall(MCL_CURRENT)) {
+ perror("mlockall(MCL_CURRENT)");
+ goto out;
+ }
+
+ if (lock_check(map))
+ goto unmap;
+
+ if (munlockall2_(MCL_CURRENT)) {
+ perror("munlockall2(MCL_CURRENT)");
+ goto unmap;
+ }
+
+ if (unlock_lock_check(map))
+ goto unmap;
+
+ munmap(map, 2 * page_size);
+
+ map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
+
+ if (map == MAP_FAILED) {
+ perror("test_munlockall second mmap");
+ goto out;
+ }
+
+ if (mlockall(MCL_ONFAULT)) {
+ perror("mlockall(MCL_ONFAULT)");
+ goto unmap;
+ }
+
+ if (onfault_check(map))
+ goto unmap;
+
+ if (munlockall2_(MCL_ONFAULT)) {
+ perror("munlockall2(MCL_ONFAULT)");
+ goto unmap;
+ }
+
+ if (unlock_onfault_check(map))
+ goto unmap;
+
+ if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
+ perror("mlockall(MCL_CURRENT | MCL_FUTURE)");
+ goto out;
+ }
+
+ if (lock_check(map))
+ goto unmap;
+
+ if (munlockall2_(MCL_FUTURE | MCL_ONFAULT)) {
+ perror("munlockall2(MCL_FUTURE | MCL_ONFAULT)");
+ goto unmap;
+ }
+
+ ret = lock_check(map);
+
+unmap:
+ munmap(map, 2 * page_size);
+out:
+ munlockall2_(MCL_CURRENT | MCL_FUTURE | MCL_ONFAULT);
+ return ret;
+}
+
+int main(char **argv, int argc)
+{
+ int ret = 0;
+ ret += test_mlock_lock();
+ ret += test_mlock_onfault();
+ ret += test_munlockall();
+ ret += test_munlock_mismatch();
+ ret += test_lock_onfault_of_present();
+ return ret;
+}
+
diff --git a/tools/testing/selftests/vm/on-fault-limit.c b/tools/testing/selftests/vm/on-fault-limit.c
new file mode 100644
index 0000000..ed2a109
--- /dev/null
+++ b/tools/testing/selftests/vm/on-fault-limit.c
@@ -0,0 +1,47 @@
+#include <sys/mman.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#ifndef MCL_ONFAULT
+#define MCL_ONFAULT (MCL_FUTURE << 1)
+#endif
+
+static int test_limit(void)
+{
+ int ret = 1;
+ struct rlimit lims;
+ void *map;
+
+ if (getrlimit(RLIMIT_MEMLOCK, &lims)) {
+ perror("getrlimit");
+ return ret;
+ }
+
+ if (mlockall(MCL_ONFAULT)) {
+ perror("mlockall");
+ return ret;
+ }
+
+ map = mmap(NULL, 2 * lims.rlim_max, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, 0, 0);
+ if (map != MAP_FAILED)
+ printf("mmap should have failed, but didn't\n");
+ else {
+ ret = 0;
+ munmap(map, 2 * lims.rlim_max);
+ }
+
+ munlockall();
+ return ret;
+}
+
+int main(int argc, char **argv)
+{
+ int ret = 0;
+
+ ret += test_limit();
+ return ret;
+}
diff --git a/tools/testing/selftests/vm/run_vmtests b/tools/testing/selftests/vm/run_vmtests
index 49ece11..990a61f 100755
--- a/tools/testing/selftests/vm/run_vmtests
+++ b/tools/testing/selftests/vm/run_vmtests
@@ -102,4 +102,37 @@ else
echo "[PASS]"
fi
+echo "--------------------"
+echo "running lock-on-fault"
+echo "--------------------"
+./lock-on-fault
+if [ $? -ne 0 ]; then
+ echo "[FAIL]"
+ exitcode=1
+else
+ echo "[PASS]"
+fi
+
+echo "--------------------"
+echo "running on-fault-limit"
+echo "--------------------"
+sudo -u nobody ./on-fault-limit
+if [ $? -ne 0 ]; then
+ echo "[FAIL]"
+ exitcode=1
+else
+ echo "[PASS]"
+fi
+
+echo "--------------------"
+echo "running mlock2-tests"
+echo "--------------------"
+./mlock2-tests
+if [ $? -ne 0 ]; then
+ echo "[FAIL]"
+ exitcode=1
+else
+ echo "[PASS]"
+fi
+
exit $exitcode
--
1.9.1
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply related
* Re: [PATCH v4 1/5] nohz_full: add support for "cpu_isolated" mode
From: Paul E. McKenney @ 2015-07-21 20:36 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Chris Metcalf, Gilad Ben Yossef, Steven Rostedt, Ingo Molnar,
Peter Zijlstra, Andrew Morton, Rik van Riel, Tejun Heo,
Frederic Weisbecker, Thomas Gleixner, Christoph Lameter,
Viresh Kumar, linux-doc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Linux API, linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <CALCETrVoHvofNHG81Q2Vb2i1qc7f2dy=qgkyb5NWNfUgYxhE8Q-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On Tue, Jul 21, 2015 at 12:26:17PM -0700, Andy Lutomirski wrote:
> On Tue, Jul 21, 2015 at 12:10 PM, Chris Metcalf <cmetcalf-d5a29ZRxExrQT0dZR+AlfA@public.gmane.org> wrote:
> > Sorry for the delay in responding; some other priorities came up internally.
> >
> > On 07/13/2015 05:45 PM, Andy Lutomirski wrote:
> >>
> >> On Mon, Jul 13, 2015 at 2:01 PM, Chris Metcalf <cmetcalf-d5a29ZRxExrQT0dZR+AlfA@public.gmane.org>
> >> wrote:
> >>>
> >>> On 07/13/2015 04:40 PM, Andy Lutomirski wrote:
> >>>>
> >>>> On Mon, Jul 13, 2015 at 12:57 PM, Chris Metcalf <cmetcalf-d5a29ZRxExrQT0dZR+AlfA@public.gmane.org>
> >>>>
> >>>> wrote:
> >>>>>
> >>>>> The existing nohz_full mode makes tradeoffs to minimize userspace
> >>>>> interruptions while still attempting to avoid overheads in the
> >>>>> kernel entry/exit path, to provide 100% kernel semantics, etc.
> >>>>>
> >>>>> However, some applications require a stronger commitment from the
> >>>>> kernel to avoid interruptions, in particular userspace device
> >>>>> driver style applications, such as high-speed networking code.
> >>>>>
> >>>>> This change introduces a framework to allow applications to elect
> >>>>> to have the stronger semantics as needed, specifying
> >>>>> prctl(PR_SET_CPU_ISOLATED, PR_CPU_ISOLATED_ENABLE) to do so.
> >>>>> Subsequent commits will add additional flags and additional
> >>>>> semantics.
> >>>>
> >>>> I thought the general consensus was that this should be the default
> >>>> behavior and that any associated bugs should be fixed.
> >>>
> >>>
> >>> I think it comes down to dividing the set of use cases in two:
> >>>
> >>> - "Regular" nohz_full, as used to improve performance and limit
> >>> interruptions, possibly for power benefits, etc. But, stray
> >>> interrupts are not particularly bad, and you don't want to take
> >>> extreme measures to avoid them.
> >>>
> >>> - What I'm calling "cpu_isolated" mode where when you return to
> >>> userspace, you expect that by God, the kernel doesn't interrupt you
> >>> again, and if it does, it's a flat-out bug.
> >>>
> >>> There are a few things that cpu_isolated mode currently does to
> >>> accomplish its goals that are pretty heavy-weight:
> >>>
> >>> Processes are held in kernel space until ticks are quiesced; this is
> >>> not necessarily what every nohz_full task wants. If a task makes a
> >>> kernel call, there may well be arbitrary timer fallout, and having a
> >>> way to select whether or not you are willing to take a timer tick after
> >>> return to userspace is pretty important.
> >>
> >> Then shouldn't deferred work be done immediately in nohz_full mode
> >> regardless? What is this delayed work that's being done?
> >
> > I'm thinking of things like needing to wait for an RCU quiesce
> > period to complete.
>
> rcu_nocbs does this, right?
CONFIG_RCU_NOCB_CPUS offloads the RCU callbacks to a kthread, which
allows the nohz CPU to turn off its scheduling-clock tick more frequently.
Chris might have some other reason to wait for an RCU grace period, given
that waiting for an RCU grace period would not guarantee no callbacks.
Some more might have arrived in the meantime, and there can be some delay
between the end of the grace period and the invocation of the callbacks.
> > In the current version, there's also the vmstat_update() that
> > may schedule delayed work and interrupt the core again
> > shortly before realizing that there are no more counter updates
> > happening, at which point it quiesces. Currently we handle
> > this in cpu_isolated mode simply by spinning and waiting for
> > the timer interrupts to complete.
>
> Perhaps we should fix that?
Didn't Christoph Lameter fix this? Or is this an additional problem?
Thanx, Paul
> >>> Likewise, there are things that you may want to do on return to
> >>> userspace that are designed to prevent further interruptions in
> >>> cpu_isolated mode, even at a possible future performance cost if and
> >>> when you return to the kernel, such as flushing the per-cpu free page
> >>> list so that you won't be interrupted by an IPI to flush it later.
> >>
> >> Why not just kick the per-cpu free page over to whatever cpu is
> >> monitoring your RCU state, etc? That should be very quick.
> >
> >
> > So just for the sake of precision, the thing I'm talking about
> > is the lru_add_drain() call on kernel exit. Are you proposing
> > that we call that for every nohz_full core on kernel exit?
> > I'm not opposed to this, but I don't know if other nohz
> > developers feel like this is the right tradeoff.
>
> I'm proposing either that we do that or that we arrange for other cpus
> to be able to steal our LRU list while we're in RCU user/idle.
>
> >> Let's fix them instead of adding new ABIs to work around them.
> >
> >
> > Well, in principle if we accepted my proposed patch series
> > and then over time came to decide that it was reasonable
> > for nohz_full to have these complete cpu isolation
> > semantics, the one proposed ABI simply becomes a no-op.
> > So it's not as problematic an ABI as some.
>
> What if we made it a debugfs thing instead of a prctl? Have a mode
> where the system tries really hard to quiesce itself even at the cost
> of performance.
>
> >
> > My issue is this: I'm totally happy with submitting a revised
> > patch series that does all the stuff for pure nohz_full that
> > I'm currently proposing for cpu_isolated. But, is it what
> > the community wants? Should I propose it and see?
> >
> > Frederic, do you have any insight here? Thanks!
> >
> > --
> > Chris Metcalf, EZChip Semiconductor
> > http://www.ezchip.com
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-api" in
> > the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
>
>
> --
> Andy Lutomirski
> AMA Capital Management, LLC
>
^ permalink raw reply
* Re: [PATCH V4 2/6] mm: mlock: Add new mlock, munlock, and munlockall system calls
From: Andrew Morton @ 2015-07-21 20:44 UTC (permalink / raw)
To: Eric B Munson
Cc: Michal Hocko, Vlastimil Babka, Heiko Carstens, Geert Uytterhoeven,
Catalin Marinas, Stephen Rothwell, Guenter Roeck, linux-alpha,
linux-kernel, linux-arm-kernel, adi-buildroot-devel,
linux-cris-kernel, linux-ia64, linux-m68k, linux-mips,
linux-am33-list, linux-parisc, linuxppc-dev, linux-s390, linux-sh,
sparclinux, linux-xtensa, linux-api, linux-arch, linux-mm
In-Reply-To: <1437508781-28655-3-git-send-email-emunson@akamai.com>
On Tue, 21 Jul 2015 15:59:37 -0400 Eric B Munson <emunson@akamai.com> wrote:
> With the refactored mlock code, introduce new system calls for mlock,
> munlock, and munlockall. The new calls will allow the user to specify
> what lock states are being added or cleared. mlock2 and munlock2 are
> trivial at the moment, but a follow on patch will add a new mlock state
> making them useful.
>
> munlock2 addresses a limitation of the current implementation. If a
> user calls mlockall(MCL_CURRENT | MCL_FUTURE) and then later decides
> that MCL_FUTURE should be removed, they would have to call munlockall()
> followed by mlockall(MCL_CURRENT) which could potentially be very
> expensive. The new munlockall2 system call allows a user to simply
> clear the MCL_FUTURE flag.
This is hard. Maybe we shouldn't have wired up anything other than
x86. That's what we usually do with new syscalls.
You appear to have missed
mm-mlock-add-new-mlock-munlock-and-munlockall-system-calls-fix.patch:
--- a/arch/arm64/include/asm/unistd.h~mm-mlock-add-new-mlock-munlock-and-munlockall-system-calls-fix
+++ a/arch/arm64/include/asm/unistd.h
@@ -44,7 +44,7 @@
#define __ARM_NR_compat_cacheflush (__ARM_NR_COMPAT_BASE+2)
#define __ARM_NR_compat_set_tls (__ARM_NR_COMPAT_BASE+5)
-#define __NR_compat_syscalls 388
+#define __NR_compat_syscalls 391
#endif
#define __ARCH_WANT_SYS_CLONE
And mm-mlock-add-new-mlock-munlock-and-munlockall-system-calls-fix-2.patch:
From: Heiko Carstens <heiko.carstens@de.ibm.com>
Subject: mm-mlock-add-new-mlock-munlock-and-munlockall-system-calls-fix-2
can we just remove the s390 bits which cause the breakage?
I will wire up the syscalls as soon as the patch set gets merged.
Heiko Carstens <heiko.carstens@de.ibm.com>
Cc: Eric B Munson <emunson@akamai.com>
Cc: Stephen Rothwell <sfr@canb.auug.org.au>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
---
arch/s390/kernel/syscalls.S | 3 ---
1 file changed, 3 deletions(-)
diff -puN arch/s390/kernel/syscalls.S~mm-mlock-add-new-mlock-munlock-and-munlockall-system-calls-fix-2 arch/s390/kernel/syscalls.S
--- a/arch/s390/kernel/syscalls.S~mm-mlock-add-new-mlock-munlock-and-munlockall-system-calls-fix-2
+++ a/arch/s390/kernel/syscalls.S
@@ -363,6 +363,3 @@ SYSCALL(sys_bpf,compat_sys_bpf)
SYSCALL(sys_s390_pci_mmio_write,compat_sys_s390_pci_mmio_write)
SYSCALL(sys_s390_pci_mmio_read,compat_sys_s390_pci_mmio_read)
SYSCALL(sys_execveat,compat_sys_execveat)
-SYSCALL(sys_mlock2,compat_sys_mlock2) /* 355 */
-SYSCALL(sys_munlock2,compat_sys_munlock2)
-SYSCALL(sys_munlockall2,compat_sys_munlockall2)
^ permalink raw reply
* Re: [RFC 1/1 v2] Input: Add ps2emu module
From: Dmitry Torokhov @ 2015-07-21 20:46 UTC (permalink / raw)
To: Stephen Chandler Paul
Cc: Andrew Morton, Mauro Carvalho Chehab, Greg KH, Arnd Bergmann,
Joe Perches, Jiri Slaby, Vishnu Patekar, Sebastian Ott, linux-doc,
linux-kernel, linux-input, linux-api, Benjamin Tissoires,
Hans de Goede
In-Reply-To: <1437508037-13928-1-git-send-email-cpaul@redhat.com>
Hi Stephen,
On Tue, Jul 21, 2015 at 03:47:17PM -0400, Stephen Chandler Paul wrote:
> Debugging input devices, specifically laptop touchpads, can be tricky
> without having the physical device handy. Here we try to remedy that
> with ps2emu. This module allows an application to connect to a character
> device provided by the kernel, and simulate any PS/2 device. In
> combination with userspace programs that can record PS/2 devices and
> replay them through the /dev/ps2emu device, this allows developers to
> debug driver issues on the PS/2 level with devices simply by requesting
> a recording from the user experiencing the issue without having to have
> the physical hardware in front of them.
This does not seem to be limited to PS/2, why not userio to keep it in
the vein of uinput, uhid, etc?
>
> Signed-off-by: Stephen Chandler Paul <cpaul@redhat.com>
> Reviewed-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> ---
> Changes
> * Remove PS2EMU_MINOR, use MISC_DYNAMIC_MINOR
> * Remove ps2emu_warn(), just use dev_warn()
> * Don't return value from copy_to_user(), return -EFAULT
> * Remove usages of unlikely()
> * Remove call to nonseekable_open()
>
> Things I didn't change
> * Didn't rename this_device, I might have misinterpreted what you were saying
> but this_device is a member of a struct that isn't defined in any of my own
> patches. I could have renamed ps2emu_misc and ps2emu_fops to misc and fops,
> but I'm guessing that's the wrong thing to do if I go off the style of the
> other driver files in the kernel tree (in drivers/input, anyway).
>
> Documentation/input/ps2emu.txt | 72 ++++++++++++
> MAINTAINERS | 6 +
> drivers/input/serio/Kconfig | 10 ++
> drivers/input/serio/Makefile | 1 +
> drivers/input/serio/ps2emu.c | 250 +++++++++++++++++++++++++++++++++++++++++
> include/uapi/linux/ps2emu.h | 42 +++++++
> 6 files changed, 381 insertions(+)
> create mode 100644 Documentation/input/ps2emu.txt
> create mode 100644 drivers/input/serio/ps2emu.c
> create mode 100644 include/uapi/linux/ps2emu.h
>
> diff --git a/Documentation/input/ps2emu.txt b/Documentation/input/ps2emu.txt
> new file mode 100644
> index 0000000..560298c
> --- /dev/null
> +++ b/Documentation/input/ps2emu.txt
> @@ -0,0 +1,72 @@
> + The ps2emu Protocol
> + (c) 2015 Stephen Chandler Paul <thatslyude@gmail.com>
> + Sponsored by Red Hat
> +--------------------------------------------------------------------------------
> +
> +1. Introduction
> +~~~~~~~~~~~~~~~
> + This module is intended to try to make the lives of input driver developers
> +easier by allowing them to test various PS/2 devices (mainly the various
> +touchpads found on laptops) without having to have the physical device in front
> +of them. ps2emu accomplishes this by allowing any privileged userspace program
> +to directly interact with the kernel's serio driver and pretend to be a PS/2
> +device.
> +
> +2. Usage overview
> +~~~~~~~~~~~~~~~~~
> + In order to interact with the ps2emu kernel module, one simply opens the
> +/dev/ps2emu character device in their applications. Commands are sent to the
> +kernel module by writing to the device, and any data received from the serio
> +driver is read as-is from the /dev/ps2emu device. All of the structures and
> +macros you need to interact with the device are defined in <linux/ps2emu.h>.
> +
> +3. Command Structure
> +~~~~~~~~~~~~~~~~~~~~
> + The struct used for sending commands to /dev/ps2emu is as follows:
> +
> + struct ps2emu_cmd {
> + __u8 type;
> + __u8 data;
> + };
> +
> + "type" describes the type of command that is being sent. This can be any one
> +of the PS2EMU_CMD macros defined in <linux/ps2emu.h>. "data" is the argument
> +that goes along with the command. In the event that the command doesn't have an
> +argument, this field can be left untouched and will be ignored by the kernel.
> +Each command should be sent by writing the struct directly to the character
> +device. In the event that the command you send is invalid, an error will be
> +returned by the character device and a more descriptive error will be printed
> +to the kernel log. Only one command can be sent at a time, any additional data
> +written to the character device after the initial command will be ignored.
> + To close the virtual PS/2 port, just close /dev/ps2emu.
> +
> +4. Commands
> +~~~~~~~~~~~
> +
> +4.1 PS2EMU_CMD_REGISTER
> +~~~~~~~~~~~~~~~~~~~~~~~
> + Registers the port with the serio driver and begins transmitting data back and
> +forth. Registration can only be performed once a port type is set with
> +PS2EMU_CMD_SET_PORT_TYPE. Has no argument.
> +
> +4.2 PS2EMU_CMD_SET_PORT_TYPE
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> + Sets the type of port we're emulating, where "data" is the port type being
> +set. Can be any of the following macros from <linux/serio.h>:
> +
> + SERIO_8042
> + SERIO_8042_XL
> + SERIO_PS_PSTHRU
Why not any others?
> +
> +4.3 PS2EMU_CMD_SEND_INTERRUPT
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> + Sends an interrupt through the virtual PS/2 port to the serio driver, where
> +"data" is the interrupt data being sent.
Might want to also allow sending "flags".
> +
> +5. Userspace tools
> +~~~~~~~~~~~~~~~~~~
> + The ps2emu userspace tools are able to record PS/2 devices using some of the
> +debugging information from i8042, and play back the devices on /dev/ps2emu. The
> +latest version of these tools can be found at:
> +
> + https://github.com/Lyude/ps2emu
> diff --git a/MAINTAINERS b/MAINTAINERS
> index a226416..68a0977 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -10877,6 +10877,12 @@ S: Maintained
> F: drivers/media/v4l2-core/videobuf2-*
> F: include/media/videobuf2-*
>
> +VIRTUAL PS/2 DEVICE DRIVER
> +M: Stephen Chandler Paul <thatslyude@gmail.com>
> +S: Maintained
> +F: drivers/input/serio/ps2emu.c
> +F: include/uapi/linux/ps2emu.h
> +
> VIRTIO CONSOLE DRIVER
> M: Amit Shah <amit.shah@redhat.com>
> L: virtualization@lists.linux-foundation.org
> diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
> index 200841b..cc3563f 100644
> --- a/drivers/input/serio/Kconfig
> +++ b/drivers/input/serio/Kconfig
> @@ -292,4 +292,14 @@ config SERIO_SUN4I_PS2
> To compile this driver as a module, choose M here: the
> module will be called sun4i-ps2.
>
> +config PS2EMU
> + tristate "Virtual PS/2 device support"
> + help
> + Say Y here if you want to emulate PS/2 devices using the ps2emu tools.
> +
> + To compile this driver as a module, choose M here: the module will be
> + called ps2emu.
> +
> + If you are unsure, say N.
> +
> endif
> diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile
> index c600089..7b20936 100644
> --- a/drivers/input/serio/Makefile
> +++ b/drivers/input/serio/Makefile
> @@ -30,3 +30,4 @@ obj-$(CONFIG_SERIO_APBPS2) += apbps2.o
> obj-$(CONFIG_SERIO_OLPC_APSP) += olpc_apsp.o
> obj-$(CONFIG_HYPERV_KEYBOARD) += hyperv-keyboard.o
> obj-$(CONFIG_SERIO_SUN4I_PS2) += sun4i-ps2.o
> +obj-$(CONFIG_PS2EMU) += ps2emu.o
> diff --git a/drivers/input/serio/ps2emu.c b/drivers/input/serio/ps2emu.c
> new file mode 100644
> index 0000000..73bf389
> --- /dev/null
> +++ b/drivers/input/serio/ps2emu.c
> @@ -0,0 +1,250 @@
> +/*
> + * ps2emu kernel PS/2 device emulation module
> + * Copyright (C) 2015 Red Hat
> + * Copyright (C) 2015 Stephen Chandler Paul <thatslyude@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License as published by the
> + * Free Software Foundation; either version 2 of the License, or (at your
> + * option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
> + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
> + * details.
> + */
> +#include <linux/circ_buf.h>
> +#include <linux/mutex.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/serio.h>
> +#include <linux/libps2.h>
> +#include <linux/slab.h>
> +#include <linux/fs.h>
> +#include <linux/miscdevice.h>
> +#include <linux/sched.h>
> +#include <linux/poll.h>
> +#include <uapi/linux/ps2emu.h>
> +
> +#define PS2EMU_NAME "ps2emu"
> +#define PS2EMU_BUFSIZE 16
> +
> +static const struct file_operations ps2emu_fops;
> +static struct miscdevice ps2emu_misc;
> +
> +struct ps2emu_device {
> + struct serio serio;
Do not embed serio into your structire but allocate separately as serio
is refcounted and you do not have exact control over when it will be
released.
> +
> + bool running;
> +
> + u8 head;
> + u8 tail;
> + unsigned char buf[PS2EMU_BUFSIZE];
> +
> + wait_queue_head_t waitq;
> +};
> +
> +/**
> + * ps2emu_device_write - Write data from serio to a ps2emu device in userspace
> + * @id: The serio port for the ps2emu device
> + * @val: The data to write to the device
> + */
> +static int ps2emu_device_write(struct serio *id, unsigned char val)
> +{
> + struct ps2emu_device *ps2emu = id->port_data;
> + u8 newhead;
> +
> + ps2emu->buf[ps2emu->head] = val;
> +
> + newhead = ps2emu->head + 1;
> +
> + if (newhead < PS2EMU_BUFSIZE)
> + ps2emu->head = newhead;
> + else
> + ps2emu->head = 0;
Why not
ps2emu->head = (ps2emu->head + 1) % PS2EMU_BUFSIZE;
given your chosen bufsize it shoudl be optimized to increment and
bitwise and.
You need locking though.
> +
> + if (newhead == ps2emu->tail)
> + dev_warn(ps2emu_misc.this_device,
> + "Buffer overflowed, ps2emu client isn't keeping up");
> +
> + wake_up_interruptible(&ps2emu->waitq);
> +
> + return 0;
> +}
> +
> +static int ps2emu_char_open(struct inode *inode, struct file *file)
> +{
> + struct ps2emu_device *ps2emu = NULL;
> +
> + ps2emu = kzalloc(sizeof(struct ps2emu_device), GFP_KERNEL);
> + if (!ps2emu)
> + return -ENOMEM;
> +
> + init_waitqueue_head(&ps2emu->waitq);
> +
> + ps2emu->serio.write = ps2emu_device_write;
> + ps2emu->serio.port_data = ps2emu;
> +
> + file->private_data = ps2emu;
> +
> + return 0;
> +}
> +
> +static int ps2emu_char_release(struct inode *inode, struct file *file)
> +{
> + struct ps2emu_device *ps2emu = file->private_data;
> +
> + /*
> + * We can rely on serio_unregister_port() to free the ps2emu struct on
> + * it's own
> + */
> + if (ps2emu->running)
> + serio_unregister_port(&ps2emu->serio);
> + else
> + kfree(ps2emu);
> +
> + return 0;
> +}
> +
> +static ssize_t ps2emu_char_read(struct file *file, char __user *buffer,
> + size_t count, loff_t *ppos)
> +{
> + struct ps2emu_device *ps2emu = file->private_data;
> + int ret;
> + size_t nonwrap_len, copylen;
> + u8 head; /* So we only access ps2emu->head once */
Why is it important?
> +
> + if (file->f_flags & O_NONBLOCK) {
> + head = ps2emu->head;
> +
> + if (head == ps2emu->tail)
> + return -EAGAIN;
> + } else {
> + ret = wait_event_interruptible(
> + ps2emu->waitq, (head = ps2emu->head) != ps2emu->tail);
If I understand it correctly you need to treat blocking read with 0
length properly: it should not wait.
> +
> + if (ret)
> + return ret;
> + }
> +
> + nonwrap_len = CIRC_CNT_TO_END(head, ps2emu->tail, PS2EMU_BUFSIZE);
> + copylen = min(nonwrap_len, count);
> +
> + if (copy_to_user(buffer, &ps2emu->buf[ps2emu->tail], copylen))
> + return -EFAULT;
> +
> + ps2emu->tail += copylen;
> + if (ps2emu->tail == PS2EMU_BUFSIZE)
> + ps2emu->tail = 0;
Locking needed gain - you can have several readers.
> +
> + return copylen;
> +}
> +
> +static ssize_t ps2emu_char_write(struct file *file, const char __user *buffer,
> + size_t count, loff_t *ppos)
> +{
> + struct ps2emu_device *ps2emu = file->private_data;
> + struct ps2emu_cmd cmd;
> +
> + if (count < sizeof(cmd))
> + return -EINVAL;
> +
> + if (copy_from_user(&cmd, buffer, sizeof(cmd)))
> + return -EFAULT;
> +
> + switch (cmd.type) {
> + case PS2EMU_CMD_REGISTER:
> + if (!ps2emu->serio.id.type) {
> + dev_warn(ps2emu_misc.this_device,
> + "No port type given on /dev/ps2emu\n");
> +
> + return -EINVAL;
> + }
> + if (ps2emu->running) {
> + dev_warn(ps2emu_misc.this_device,
> + "Begin command sent, but we're already running\n");
> +
> + return -EINVAL;
> + }
> +
> + ps2emu->running = true;
> + serio_register_port(&ps2emu->serio);
> + break;
> +
> + case PS2EMU_CMD_SET_PORT_TYPE:
> + if (ps2emu->running) {
> + dev_warn(ps2emu_misc.this_device,
> + "Can't change port type on an already running ps2emu instance\n");
> +
> + return -EINVAL;
> + }
> +
> + switch (cmd.data) {
> + case SERIO_8042:
> + case SERIO_8042_XL:
> + case SERIO_PS_PSTHRU:
> + ps2emu->serio.id.type = cmd.data;
> + break;
> +
> + default:
> + dev_warn(ps2emu_misc.this_device,
> + "Invalid port type 0x%hhx\n", cmd.data);
Why not allow others?
> +
> + return -EINVAL;
> + }
> +
> + break;
> +
> + case PS2EMU_CMD_SEND_INTERRUPT:
> + if (!ps2emu->running) {
> + dev_warn(ps2emu_misc.this_device,
> + "The device must be registered before sending interrupts\n");
> +
> + return -EINVAL;
> + }
> +
> + serio_interrupt(&ps2emu->serio, cmd.data, 0);
> +
> + break;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return sizeof(cmd);
> +}
> +
> +static unsigned int ps2emu_char_poll(struct file *file, poll_table *wait)
> +{
> + struct ps2emu_device *ps2emu = file->private_data;
> +
> + poll_wait(file, &ps2emu->waitq, wait);
> +
> + if (ps2emu->head != ps2emu->tail)
> + return POLLIN | POLLRDNORM;
> +
> + return 0;
> +}
> +
> +static const struct file_operations ps2emu_fops = {
> + .owner = THIS_MODULE,
> + .open = ps2emu_char_open,
> + .release = ps2emu_char_release,
> + .read = ps2emu_char_read,
> + .write = ps2emu_char_write,
> + .poll = ps2emu_char_poll,
> + .llseek = no_llseek,
> +};
> +
> +static struct miscdevice ps2emu_misc = {
> + .fops = &ps2emu_fops,
> + .minor = MISC_DYNAMIC_MINOR,
> + .name = PS2EMU_NAME,
> +};
> +
> +MODULE_AUTHOR("Stephen Chandler Paul <thatslyude@gmail.com>");
> +MODULE_DESCRIPTION("ps2emu");
> +MODULE_LICENSE("GPL");
> +
> +module_driver(ps2emu_misc, misc_register, misc_deregister);
> diff --git a/include/uapi/linux/ps2emu.h b/include/uapi/linux/ps2emu.h
> new file mode 100644
> index 0000000..63f5cc9
> --- /dev/null
> +++ b/include/uapi/linux/ps2emu.h
> @@ -0,0 +1,42 @@
> +/*
> + * ps2emu.h
> + * Copyright (C) 2015 Red Hat
> + * Copyright (C) 2015 Lyude (Stephen Chandler Paul) <cpaul@redhat.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License as published by the
> + * Free Software Foundation; either version 2 of the License, or (at your
> + * option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
> + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
> + * details.
> + *
> + * This is the public header used for user-space communication with the ps2emu
> + * driver. __attribute__((__packed__)) is used for all structs to keep ABI
> + * compatibility between all architectures.
> + */
> +
> +#ifndef _PS2EMU_H
> +#define _PS2EMU_H
> +
> +#include <linux/types.h>
> +
> +#define PS2EMU_CMD_REGISTER 0
> +#define PS2EMU_CMD_SET_PORT_TYPE 1
> +#define PS2EMU_CMD_SEND_INTERRUPT 2
> +
> +/*
> + * ps2emu Commands
> + * All commands sent to /dev/ps2emu are encoded using this structure. The type
> + * field should contain a PS2EMU_CMD* value that indicates what kind of command
> + * is being sent to ps2emu. The data field should contain the accompanying
> + * argument for the command, if there is one.
> + */
> +struct ps2emu_cmd {
> + __u8 type;
> + __u8 data;
> +} __attribute__((__packed__));
> +
> +#endif /* !_PS2EMU_H */
> --
> 2.4.3
>
Thanks.
--
Dmitry
^ permalink raw reply
* Re: [RFC] ps2emu - PS/2 emulation module
From: Dmitry Torokhov @ 2015-07-21 20:48 UTC (permalink / raw)
To: Stephen Chandler Paul
Cc: Andrew Morton, Mauro Carvalho Chehab, Greg KH, Arnd Bergmann,
Joe Perches, Jiri Slaby, Vishnu Patekar, Sebastian Ott, linux-doc,
linux-kernel, linux-input, linux-api, Benjamin Tissoires,
Hans de Goede
In-Reply-To: <1437505634-8633-1-git-send-email-cpaul@redhat.com>
On Tue, Jul 21, 2015 at 03:07:13PM -0400, Stephen Chandler Paul wrote:
>
> Right now we make use of this module with the ps2emu userland tools[2] that I've
> wrote recently. Recording of PS/2 devices is done by enabling the debugging
> output from i8042, triggering a rescan of the PS/2 ports, and doing some clever
> trimming of data to remove anything that goes across the i8042 chip that isn't
> relevant to the PS/2 protocol. The replay application is pretty simple, and just
There is also serio_raw module that might be easier to use for recording
raw AUX data.
Thanks.
--
Dmitry
^ permalink raw reply
* Re: [PATCH -mm v9 0/8] idle memory tracking
From: Andres Lagar-Cavilla @ 2015-07-21 21:39 UTC (permalink / raw)
To: Vladimir Davydov
Cc: Andrew Morton, Minchan Kim, Raghavendra K T, Johannes Weiner,
Michal Hocko, Greg Thelen, Michel Lespinasse, David Rientjes,
Pavel Emelyanov, Cyrill Gorcunov, Jonathan Corbet, linux-api,
linux-doc, linux-mm, cgroups, linux-kernel
In-Reply-To: <cover.1437303956.git.vdavydov@parallels.com>
[-- Attachment #1: Type: text/plain, Size: 14247 bytes --]
On Sun, Jul 19, 2015 at 5:31 AM, Vladimir Davydov <vdavydov@parallels.com>
wrote:
> Hi,
>
> This patch set introduces a new user API for tracking user memory pages
> that have not been used for a given period of time. The purpose of this
> is to provide the userspace with the means of tracking a workload's
> working set, i.e. the set of pages that are actively used by the
> workload. Knowing the working set size can be useful for partitioning
> the system more efficiently, e.g. by tuning memory cgroup limits
> appropriately, or for job placement within a compute cluster.
>
> It is based on top of v4.2-rc2-mmotm-2015-07-15-16-46
> It applies without conflicts to v4.2-rc2-mmotm-2015-07-17-16-04 as well
>
> ---- USE CASES ----
>
> The unified cgroup hierarchy has memory.low and memory.high knobs, which
> are defined as the low and high boundaries for the workload working set
> size. However, the working set size of a workload may be unknown or
> change in time. With this patch set, one can periodically estimate the
> amount of memory unused by each cgroup and tune their memory.low and
> memory.high parameters accordingly, therefore optimizing the overall
> memory utilization.
>
> Another use case is balancing workloads within a compute cluster.
> Knowing how much memory is not really used by a workload unit may help
> take a more optimal decision when considering migrating the unit to
> another node within the cluster.
>
> Also, as noted by Minchan, this would be useful for per-process reclaim
> (https://lwn.net/Articles/545668/). With idle tracking, we could reclaim
> idle
> pages only by smart user memory manager.
>
> ---- USER API ----
>
> The user API consists of two new proc files:
>
> * /proc/kpageidle. This file implements a bitmap where each bit
> corresponds
> to a page, indexed by PFN. When the bit is set, the corresponding page
> is
> idle. A page is considered idle if it has not been accessed since it was
> marked idle. To mark a page idle one should set the bit corresponding
> to the
> page by writing to the file. A value written to the file is OR-ed with
> the
> current bitmap value. Only user memory pages can be marked idle, for
> other
> page types input is silently ignored. Writing to this file beyond max
> PFN
> results in the ENXIO error. Only available when
> CONFIG_IDLE_PAGE_TRACKING is
> set.
>
> This file can be used to estimate the amount of pages that are not
> used by a particular workload as follows:
>
> 1. mark all pages of interest idle by setting corresponding bits in the
> /proc/kpageidle bitmap
> 2. wait until the workload accesses its working set
> 3. read /proc/kpageidle and count the number of bits set
>
> * /proc/kpagecgroup. This file contains a 64-bit inode number of the
> memory cgroup each page is charged to, indexed by PFN. Only available
> when
> CONFIG_MEMCG is set.
>
> This file can be used to find all pages (including unmapped file
> pages) accounted to a particular cgroup. Using /proc/kpageidle, one
> can then estimate the cgroup working set size.
>
> For an example of using these files for estimating the amount of unused
> memory pages per each memory cgroup, please see the script attached
> below.
>
> ---- REASONING ----
>
> The reason to introduce the new user API instead of using
> /proc/PID/{clear_refs,smaps} is that the latter has two serious
> drawbacks:
>
> - it does not count unmapped file pages
> - it affects the reclaimer logic
>
> The new API attempts to overcome them both. For more details on how it
> is achieved, please see the comment to patch 6.
>
> ---- CHANGE LOG ----
>
> Changes in v9:
>
> - add cond_resched to /proc/kpage* read/write loop (Andres)
> - rebase on top of v4.2-rc2-mmotm-2015-07-15-16-46
>
And thanks for the perf report.
This series
Reviewed-by: Andres Lagar-Cavilla <andreslc@google.com>
> Changes in v8:
>
> - clear referenced/accessed bit in secondary ptes while accessing
> /proc/kpageidle; this is required to estimate wss of KVM VMs (Andres)
> - check the young flag when collapsing a huge page
> - copy idle/young flags on page migration
>
> Changes in v7:
>
> This iteration addresses Andres's comments to v6:
>
> - do not reuse page_referenced for clearing idle flag, introduce a
> separate function instead; this way we won't issue expensive tlb
> flushes on /proc/kpageidle read/write
> - propagate young/idle flags from head to tail pages on thp split
> - skip compound tail pages while reading/writing /proc/kpageidle
> - cleanup page_referenced_one
>
> Changes in v6:
>
> - Split the patch introducing page_cgroup_ino helper to ease review.
> - Rebase on top of v4.1-rc7-mmotm-2015-06-09-16-55
>
> Changes in v5:
>
> - Fix possible race between kpageidle_clear_pte_refs() and
> __page_set_anon_rmap() by checking that a page is on an LRU list
> under zone->lru_lock (Minchan).
> - Export idle flag via /proc/kpageflags (Minchan).
> - Rebase on top of 4.1-rc3.
>
> Changes in v4:
>
> This iteration primarily addresses Minchan's comments to v3:
>
> - Implement /proc/kpageidle as a bitmap instead of using u64 per each
> page,
> because there does not seem to be any future uses for the other 63 bits.
> - Do not double-increase pra->referenced in page_referenced_one() if the
> page
> was young and referenced recently.
> - Remove the pointless (page_count == 0) check from kpageidle_get_page().
> - Rename kpageidle_clear_refs() to kpageidle_clear_pte_refs().
> - Improve comments to kpageidle-related functions.
> - Rebase on top of 4.1-rc2.
>
> Note it does not address Minchan's concern of possible
> __page_set_anon_rmap vs
> page_referenced race (see https://lkml.org/lkml/2015/5/3/220) since it is
> still
> unclear if this race can really happen (see
> https://lkml.org/lkml/2015/5/4/160)
>
> Changes in v3:
>
> - Enable CONFIG_IDLE_PAGE_TRACKING for 32 bit. Since this feature
> requires two extra page flags and there is no space for them on 32
> bit, page ext is used (thanks to Minchan Kim).
> - Minor code cleanups and comments improved.
> - Rebase on top of 4.1-rc1.
>
> Changes in v2:
>
> - The main difference from v1 is the API change. In v1 the user can
> only set the idle flag for all pages at once, and for clearing the
> Idle flag on pages accessed via page tables /proc/PID/clear_refs
> should be used.
> The main drawback of the v1 approach, as noted by Minchan, is that on
> big machines setting the idle flag for each pages can result in CPU
> bursts, which would be especially frustrating if the user only wanted
> to estimate the amount of idle pages for a particular process or VMA.
> With the new API a more fine-grained approach is possible: one can
> read a process's /proc/PID/pagemap and set/check the Idle flag only
> for those pages of the process's address space he or she is
> interested in.
> Another good point about the v2 API is that it is possible to limit
> /proc/kpage* scanning rate when the user wants to estimate the total
> number of idle pages, which is unachievable with the v1 approach.
> - Make /proc/kpagecgroup return the ino of the closest online ancestor
> in case the cgroup a page is charged to is offline.
> - Fix /proc/PID/clear_refs not clearing Young page flag.
> - Rebase on top of v4.0-rc6-mmotm-2015-04-01-14-54
>
> v8: https://lkml.org/lkml/2015/7/15/587
> v7: https://lkml.org/lkml/2015/7/11/119
> v6: https://lkml.org/lkml/2015/6/12/301
> v5: https://lkml.org/lkml/2015/5/12/449
> v4: https://lkml.org/lkml/2015/5/7/580
> v3: https://lkml.org/lkml/2015/4/28/224
> v2: https://lkml.org/lkml/2015/4/7/260
> v1: https://lkml.org/lkml/2015/3/18/794
>
> ---- PATCH SET STRUCTURE ----
>
> The patch set is organized as follows:
>
> - patch 1 adds page_cgroup_ino() helper for the sake of
> /proc/kpagecgroup and patches 2-3 do related cleanup
> - patch 4 adds /proc/kpagecgroup, which reports cgroup ino each page is
> charged to
> - patch 5 introduces a new mmu notifier callback, clear_young, which is
> a lightweight version of clear_flush_young; it is used in patch 6
> - patch 6 implements the idle page tracking feature, including the
> userspace API, /proc/kpageidle
> - patch 7 exports idle flag via /proc/kpageflags
>
> ---- SIMILAR WORKS ----
>
> Originally, the patch for tracking idle memory was proposed back in 2011
> by Michel Lespinasse (see http://lwn.net/Articles/459269/). The main
> difference between Michel's patch and this one is that Michel
> implemented a kernel space daemon for estimating idle memory size per
> cgroup while this patch only provides the userspace with the minimal API
> for doing the job, leaving the rest up to the userspace. However, they
> both share the same idea of Idle/Young page flags to avoid affecting the
> reclaimer logic.
>
> ---- PERFORMANCE EVALUATION ----
>
> SPECjvm2008 (https://www.spec.org/jvm2008/) was used to evaluate the
> performance impact introduced by this patch set. Three runs were carried
> out:
>
> - base: kernel without the patch
> - patched: patched kernel, the feature is not used
> - patched-active: patched kernel, 1 minute-period daemon is used for
> tracking idle memory
>
> For tracking idle memory, idlememstat utility was used:
> https://github.com/locker/idlememstat
>
> testcase base patched patched-active
>
> compiler 537.40 ( 0.00)% 532.26 (-0.96)% 538.31 ( 0.17)%
> compress 305.47 ( 0.00)% 301.08 (-1.44)% 300.71 (-1.56)%
> crypto 284.32 ( 0.00)% 282.21 (-0.74)% 284.87 ( 0.19)%
> derby 411.05 ( 0.00)% 413.44 ( 0.58)% 412.07 ( 0.25)%
> mpegaudio 189.96 ( 0.00)% 190.87 ( 0.48)% 189.42 (-0.28)%
> scimark.large 46.85 ( 0.00)% 46.41 (-0.94)% 47.83 ( 2.09)%
> scimark.small 412.91 ( 0.00)% 415.41 ( 0.61)% 421.17 ( 2.00)%
> serial 204.23 ( 0.00)% 213.46 ( 4.52)% 203.17 (-0.52)%
> startup 36.76 ( 0.00)% 35.49 (-3.45)% 35.64 (-3.05)%
> sunflow 115.34 ( 0.00)% 115.08 (-0.23)% 117.37 ( 1.76)%
> xml 620.55 ( 0.00)% 619.95 (-0.10)% 620.39 (-0.03)%
>
> composite 211.50 ( 0.00)% 211.15 (-0.17)% 211.67 ( 0.08)%
>
> time idlememstat:
>
> 17.20user 65.16system 2:15:23elapsed 1%CPU (0avgtext+0avgdata
> 8476maxresident)k
> 448inputs+40outputs (1major+36052minor)pagefaults 0swaps
>
> ---- SCRIPT FOR COUNTING IDLE PAGES PER CGROUP ----
> #! /usr/bin/python
> #
>
> import os
> import stat
> import errno
> import struct
>
> CGROUP_MOUNT = "/sys/fs/cgroup/memory"
> BUFSIZE = 8 * 1024 # must be multiple of 8
>
>
> def get_hugepage_size():
> with open("/proc/meminfo", "r") as f:
> for s in f:
> k, v = s.split(":")
> if k == "Hugepagesize":
> return int(v.split()[0]) * 1024
>
> PAGE_SIZE = os.sysconf("SC_PAGE_SIZE")
> HUGEPAGE_SIZE = get_hugepage_size()
>
>
> def set_idle():
> f = open("/proc/kpageidle", "wb", BUFSIZE)
> while True:
> try:
> f.write(struct.pack("Q", pow(2, 64) - 1))
> except IOError as err:
> if err.errno == errno.ENXIO:
> break
> raise
> f.close()
>
>
> def count_idle():
> f_flags = open("/proc/kpageflags", "rb", BUFSIZE)
> f_cgroup = open("/proc/kpagecgroup", "rb", BUFSIZE)
>
> with open("/proc/kpageidle", "rb", BUFSIZE) as f:
> while f.read(BUFSIZE): pass # update idle flag
>
> idlememsz = {}
> while True:
> s1, s2 = f_flags.read(8), f_cgroup.read(8)
> if not s1 or not s2:
> break
>
> flags, = struct.unpack('Q', s1)
> cgino, = struct.unpack('Q', s2)
>
> unevictable = (flags >> 18) & 1
> huge = (flags >> 22) & 1
> idle = (flags >> 25) & 1
>
> if idle and not unevictable:
> idlememsz[cgino] = idlememsz.get(cgino, 0) + \
> (HUGEPAGE_SIZE if huge else PAGE_SIZE)
>
> f_flags.close()
> f_cgroup.close()
> return idlememsz
>
>
> if __name__ == "__main__":
> print "Setting the idle flag for each page..."
> set_idle()
>
> raw_input("Wait until the workload accesses its working set, "
> "then press Enter")
>
> print "Counting idle pages..."
> idlememsz = count_idle()
>
> for dir, subdirs, files in os.walk(CGROUP_MOUNT):
> ino = os.stat(dir)[stat.ST_INO]
> print dir + ": " + str(idlememsz.get(ino, 0) / 1024) + " kB"
> ---- END SCRIPT ----
>
> Comments are more than welcome.
>
> Thanks,
>
> Vladimir Davydov (8):
> memcg: add page_cgroup_ino helper
> hwpoison: use page_cgroup_ino for filtering by memcg
> memcg: zap try_get_mem_cgroup_from_page
> proc: add kpagecgroup file
> mmu-notifier: add clear_young callback
> proc: add kpageidle file
> proc: export idle flag via kpageflags
> proc: add cond_resched to /proc/kpage* read/write loop
>
> Documentation/vm/pagemap.txt | 22 ++-
> fs/proc/page.c | 282
> +++++++++++++++++++++++++++++++++
> fs/proc/task_mmu.c | 4 +-
> include/linux/memcontrol.h | 10 +-
> include/linux/mm.h | 98 ++++++++++++
> include/linux/mmu_notifier.h | 44 +++++
> include/linux/page-flags.h | 11 ++
> include/linux/page_ext.h | 4 +
> include/uapi/linux/kernel-page-flags.h | 1 +
> mm/Kconfig | 12 ++
> mm/debug.c | 4 +
> mm/huge_memory.c | 11 +-
> mm/hwpoison-inject.c | 5 +-
> mm/memcontrol.c | 71 ++++-----
> mm/memory-failure.c | 16 +-
> mm/migrate.c | 5 +
> mm/mmu_notifier.c | 17 ++
> mm/page_ext.c | 3 +
> mm/rmap.c | 5 +
> mm/swap.c | 2 +
> virt/kvm/kvm_main.c | 18 +++
> 21 files changed, 579 insertions(+), 66 deletions(-)
>
> --
> 2.1.4
>
>
--
Andres Lagar-Cavilla | Google Kernel Team | andreslc@google.com
[-- Attachment #2: Type: text/html, Size: 18850 bytes --]
^ permalink raw reply
* Re: [RFC 1/1 v2] Input: Add ps2emu module
From: Stephen Chandler Paul @ 2015-07-21 21:42 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Andrew Morton, Mauro Carvalho Chehab, Greg KH, Arnd Bergmann,
Joe Perches, Jiri Slaby, Vishnu Patekar, Sebastian Ott, linux-doc,
linux-kernel, linux-input, linux-api, Benjamin Tissoires,
Hans de Goede
In-Reply-To: <20150721204611.GD39076@dtor-ws>
On Tue, 2015-07-21 at 13:46 -0700, Dmitry Torokhov wrote:
> Hi Stephen,
>
> On Tue, Jul 21, 2015 at 03:47:17PM -0400, Stephen Chandler Paul
> wrote:
> > Debugging input devices, specifically laptop touchpads, can be
> > tricky
> > without having the physical device handy. Here we try to remedy
> > that
> > with ps2emu. This module allows an application to connect to a
> > character
> > device provided by the kernel, and simulate any PS/2 device. In
> > combination with userspace programs that can record PS/2 devices
> > and
> > replay them through the /dev/ps2emu device, this allows developers
> > to
> > debug driver issues on the PS/2 level with devices simply by
> > requesting
> > a recording from the user experiencing the issue without having to
> > have
> > the physical hardware in front of them.
>
> This does not seem to be limited to PS/2, why not userio to keep it
> in
> the vein of uinput, uhid, etc?
>
> >
> > Signed-off-by: Stephen Chandler Paul <cpaul@redhat.com>
> > Reviewed-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> > ---
> > Changes
> > * Remove PS2EMU_MINOR, use MISC_DYNAMIC_MINOR
> > * Remove ps2emu_warn(), just use dev_warn()
> > * Don't return value from copy_to_user(), return -EFAULT
> > * Remove usages of unlikely()
> > * Remove call to nonseekable_open()
> >
> > Things I didn't change
> > * Didn't rename this_device, I might have misinterpreted what you
> > were saying
> > but this_device is a member of a struct that isn't defined in any
> > of my own
> > patches. I could have renamed ps2emu_misc and ps2emu_fops to misc
> > and fops,
> > but I'm guessing that's the wrong thing to do if I go off the
> > style of the
> > other driver files in the kernel tree (in drivers/input, anyway).
> >
> > Documentation/input/ps2emu.txt | 72 ++++++++++++
> > MAINTAINERS | 6 +
> > drivers/input/serio/Kconfig | 10 ++
> > drivers/input/serio/Makefile | 1 +
> > drivers/input/serio/ps2emu.c | 250
> > +++++++++++++++++++++++++++++++++++++++++
> > include/uapi/linux/ps2emu.h | 42 +++++++
> > 6 files changed, 381 insertions(+)
> > create mode 100644 Documentation/input/ps2emu.txt
> > create mode 100644 drivers/input/serio/ps2emu.c
> > create mode 100644 include/uapi/linux/ps2emu.h
> >
> > diff --git a/Documentation/input/ps2emu.txt
> > b/Documentation/input/ps2emu.txt
> > new file mode 100644
> > index 0000000..560298c
> > --- /dev/null
> > +++ b/Documentation/input/ps2emu.txt
> > @@ -0,0 +1,72 @@
> > + The ps2emu Protocol
> > + (c) 2015 Stephen Chandler Paul <thatslyude@gmail.com>
> > + Sponsored by Red Hat
> > +------------------------------------------------------------------
> > --------------
> > +
> > +1. Introduction
> > +~~~~~~~~~~~~~~~
> > + This module is intended to try to make the lives of input driver
> > developers
> > +easier by allowing them to test various PS/2 devices (mainly the
> > various
> > +touchpads found on laptops) without having to have the physical
> > device in front
> > +of them. ps2emu accomplishes this by allowing any privileged
> > userspace program
> > +to directly interact with the kernel's serio driver and pretend to
> > be a PS/2
> > +device.
> > +
> > +2. Usage overview
> > +~~~~~~~~~~~~~~~~~
> > + In order to interact with the ps2emu kernel module, one simply
> > opens the
> > +/dev/ps2emu character device in their applications. Commands are
> > sent to the
> > +kernel module by writing to the device, and any data received from
> > the serio
> > +driver is read as-is from the /dev/ps2emu device. All of the
> > structures and
> > +macros you need to interact with the device are defined in
> > <linux/ps2emu.h>.
> > +
> > +3. Command Structure
> > +~~~~~~~~~~~~~~~~~~~~
> > + The struct used for sending commands to /dev/ps2emu is as
> > follows:
> > +
> > + struct ps2emu_cmd {
> > + __u8 type;
> > + __u8 data;
> > + };
> > +
> > + "type" describes the type of command that is being sent. This
> > can be any one
> > +of the PS2EMU_CMD macros defined in <linux/ps2emu.h>. "data" is
> > the argument
> > +that goes along with the command. In the event that the command
> > doesn't have an
> > +argument, this field can be left untouched and will be ignored by
> > the kernel.
> > +Each command should be sent by writing the struct directly to the
> > character
> > +device. In the event that the command you send is invalid, an
> > error will be
> > +returned by the character device and a more descriptive error will
> > be printed
> > +to the kernel log. Only one command can be sent at a time, any
> > additional data
> > +written to the character device after the initial command will be
> > ignored.
> > + To close the virtual PS/2 port, just close /dev/ps2emu.
> > +
> > +4. Commands
> > +~~~~~~~~~~~
> > +
> > +4.1 PS2EMU_CMD_REGISTER
> > +~~~~~~~~~~~~~~~~~~~~~~~
> > + Registers the port with the serio driver and begins transmitting
> > data back and
> > +forth. Registration can only be performed once a port type is set
> > with
> > +PS2EMU_CMD_SET_PORT_TYPE. Has no argument.
> > +
> > +4.2 PS2EMU_CMD_SET_PORT_TYPE
> > +~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> > + Sets the type of port we're emulating, where "data" is the port
> > type being
> > +set. Can be any of the following macros from <linux/serio.h>:
> > +
> > + SERIO_8042
> > + SERIO_8042_XL
> > + SERIO_PS_PSTHRU
>
> Why not any others?
>
> > +
> > +4.3 PS2EMU_CMD_SEND_INTERRUPT
> > +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> > + Sends an interrupt through the virtual PS/2 port to the serio
> > driver, where
> > +"data" is the interrupt data being sent.
>
> Might want to also allow sending "flags".
I'm eventually planning on doing this, I can add something in the
current version if you want but I didn't see any immediate need for
them.
>
> > +
> > +5. Userspace tools
> > +~~~~~~~~~~~~~~~~~~
> > + The ps2emu userspace tools are able to record PS/2 devices using
> > some of the
> > +debugging information from i8042, and play back the devices on
> > /dev/ps2emu. The
> > +latest version of these tools can be found at:
> > +
> > + https://github.com/Lyude/ps2emu
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index a226416..68a0977 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -10877,6 +10877,12 @@ S: Maintained
> > F: drivers/media/v4l2-core/videobuf2-*
> > F: include/media/videobuf2-*
> >
> > +VIRTUAL PS/2 DEVICE DRIVER
> > +M: Stephen Chandler Paul <thatslyude@gmail.com>
> > +S: Maintained
> > +F: drivers/input/serio/ps2emu.c
> > +F: include/uapi/linux/ps2emu.h
> > +
> > VIRTIO CONSOLE DRIVER
> > M: Amit Shah <amit.shah@redhat.com>
> > L: virtualization@lists.linux-foundation.org
> > diff --git a/drivers/input/serio/Kconfig
> > b/drivers/input/serio/Kconfig
> > index 200841b..cc3563f 100644
> > --- a/drivers/input/serio/Kconfig
> > +++ b/drivers/input/serio/Kconfig
> > @@ -292,4 +292,14 @@ config SERIO_SUN4I_PS2
> > To compile this driver as a module, choose M here: the
> > module will be called sun4i-ps2.
> >
> > +config PS2EMU
> > + tristate "Virtual PS/2 device support"
> > + help
> > + Say Y here if you want to emulate PS/2 devices using the
> > ps2emu tools.
> > +
> > + To compile this driver as a module, choose M here: the
> > module will be
> > + called ps2emu.
> > +
> > + If you are unsure, say N.
> > +
> > endif
> > diff --git a/drivers/input/serio/Makefile
> > b/drivers/input/serio/Makefile
> > index c600089..7b20936 100644
> > --- a/drivers/input/serio/Makefile
> > +++ b/drivers/input/serio/Makefile
> > @@ -30,3 +30,4 @@ obj-$(CONFIG_SERIO_APBPS2) += apbps2.o
> > obj-$(CONFIG_SERIO_OLPC_APSP) += olpc_apsp.o
> > obj-$(CONFIG_HYPERV_KEYBOARD) += hyperv-keyboard.o
> > obj-$(CONFIG_SERIO_SUN4I_PS2) += sun4i-ps2.o
> > +obj-$(CONFIG_PS2EMU) += ps2emu.o
> > diff --git a/drivers/input/serio/ps2emu.c
> > b/drivers/input/serio/ps2emu.c
> > new file mode 100644
> > index 0000000..73bf389
> > --- /dev/null
> > +++ b/drivers/input/serio/ps2emu.c
> > @@ -0,0 +1,250 @@
> > +/*
> > + * ps2emu kernel PS/2 device emulation module
> > + * Copyright (C) 2015 Red Hat
> > + * Copyright (C) 2015 Stephen Chandler Paul <thatslyude@gmail.com>
> > + *
> > + * This program is free software; you can redistribute it and/or
> > modify it
> > + * under the terms of the GNU Lesser General Public License as
> > published by the
> > + * Free Software Foundation; either version 2 of the License, or
> > (at your
> > + * option) any later version.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > but WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of
> > MERCHANTABILITY or FITNESS
> > + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
> > License for more
> > + * details.
> > + */
> > +#include <linux/circ_buf.h>
> > +#include <linux/mutex.h>
> > +#include <linux/module.h>
> > +#include <linux/init.h>
> > +#include <linux/kernel.h>
> > +#include <linux/serio.h>
> > +#include <linux/libps2.h>
> > +#include <linux/slab.h>
> > +#include <linux/fs.h>
> > +#include <linux/miscdevice.h>
> > +#include <linux/sched.h>
> > +#include <linux/poll.h>
> > +#include <uapi/linux/ps2emu.h>
> > +
> > +#define PS2EMU_NAME "ps2emu"
> > +#define PS2EMU_BUFSIZE 16
> > +
> > +static const struct file_operations ps2emu_fops;
> > +static struct miscdevice ps2emu_misc;
> > +
> > +struct ps2emu_device {
> > + struct serio serio;
>
> Do not embed serio into your structire but allocate separately as
> serio
> is refcounted and you do not have exact control over when it will be
> released.
>
> > +
> > + bool running;
> > +
> > + u8 head;
> > + u8 tail;
> > + unsigned char buf[PS2EMU_BUFSIZE];
> > +
> > + wait_queue_head_t waitq;
> > +};
> > +
> > +/**
> > + * ps2emu_device_write - Write data from serio to a ps2emu device
> > in userspace
> > + * @id: The serio port for the ps2emu device
> > + * @val: The data to write to the device
> > + */
> > +static int ps2emu_device_write(struct serio *id, unsigned char
> > val)
> > +{
> > + struct ps2emu_device *ps2emu = id->port_data;
> > + u8 newhead;
> > +
> > + ps2emu->buf[ps2emu->head] = val;
> > +
> > + newhead = ps2emu->head + 1;
> > +
> > + if (newhead < PS2EMU_BUFSIZE)
> > + ps2emu->head = newhead;
> > + else
> > + ps2emu->head = 0;
>
> Why not
>
> ps2emu->head = (ps2emu->head + 1) % PS2EMU_BUFSIZE;
> given your chosen bufsize it shoudl be optimized to increment and
> bitwise and.
>
> You need locking though.
>
> > +
> > + if (newhead == ps2emu->tail)
> > + dev_warn(ps2emu_misc.this_device,
> > + "Buffer overflowed, ps2emu client isn't
> > keeping up");
> > +
> > + wake_up_interruptible(&ps2emu->waitq);
> > +
> > + return 0;
> > +}
> > +
> > +static int ps2emu_char_open(struct inode *inode, struct file
> > *file)
> > +{
> > + struct ps2emu_device *ps2emu = NULL;
> > +
> > + ps2emu = kzalloc(sizeof(struct ps2emu_device),
> > GFP_KERNEL);
> > + if (!ps2emu)
> > + return -ENOMEM;
> > +
> > + init_waitqueue_head(&ps2emu->waitq);
> > +
> > + ps2emu->serio.write = ps2emu_device_write;
> > + ps2emu->serio.port_data = ps2emu;
> > +
> > + file->private_data = ps2emu;
> > +
> > + return 0;
> > +}
> > +
> > +static int ps2emu_char_release(struct inode *inode, struct file
> > *file)
> > +{
> > + struct ps2emu_device *ps2emu = file->private_data;
> > +
> > + /*
> > + * We can rely on serio_unregister_port() to free the
> > ps2emu struct on
> > + * it's own
> > + */
> > + if (ps2emu->running)
> > + serio_unregister_port(&ps2emu->serio);
> > + else
> > + kfree(ps2emu);
> > +
> > + return 0;
> > +}
> > +
> > +static ssize_t ps2emu_char_read(struct file *file, char __user
> > *buffer,
> > + size_t count, loff_t *ppos)
> > +{
> > + struct ps2emu_device *ps2emu = file->private_data;
> > + int ret;
> > + size_t nonwrap_len, copylen;
> > + u8 head; /* So we only access ps2emu->head once */
>
> Why is it important?
Benjamin had mentioned that this comment might need clarification and
it looks like he was right. So, originally I did have locking but I did
some reading on Documentation/circular-buffers.txt and noticed that
there's a couple of mentions of locking not needed and I'm pretty sure
this is a situation where this is actually the case (although I should
be using ACCESS_ONCE() there).
So, there will be multiple readers. In this code however, there will
never be multiple writers. The only one ever writing to the head is
ps2emu_device_write(), and the only one ever writing to the tail is
ps2emu_char_read(). During run time, we can always expect the head to
move forward or wrap around. Going by this logic, if the head moves
forward while we're reading it, the worst that will happen is that
there will now be some data in the buffer that we won't check until we
finish up in ps2emu_char_read(). This being said, in order to be safe
we try to read from ps2emu->head only once and then work with the last
value we retrieved from there for the duration of the function, so that
the head position we're going off of in the function doesn't change
while we're using it.
All of this being said, if the head does happen to catch up to the tail
and not the other way around, data will be lost (in my testing though
this really doesn't happen at all during normal run time since most PS2
commands sequences don't go over 16 bytes). But all that means is that
the userspace application will go out of sync and not be able to
continue (which would happen anyway, since the driver can timeout
waiting for a response).
Note that I might not have explained that very well, this is the first
time I've done something like this. I don't think this behavior is bad,
but I'm new to writing drivers so I may very well be wrong :).
> > +
> > + if (file->f_flags & O_NONBLOCK) {
> > + head = ps2emu->head;
> > +
> > + if (head == ps2emu->tail)
> > + return -EAGAIN;
> > + } else {
> > + ret = wait_event_interruptible(
> > + ps2emu->waitq, (head = ps2emu->head) !=
> > ps2emu->tail);
>
> If I understand it correctly you need to treat blocking read with 0
> length properly: it should not wait.
>
> > +
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + nonwrap_len = CIRC_CNT_TO_END(head, ps2emu->tail,
> > PS2EMU_BUFSIZE);
> > + copylen = min(nonwrap_len, count);
> > +
> > + if (copy_to_user(buffer, &ps2emu->buf[ps2emu->tail],
> > copylen))
> > + return -EFAULT;
> > +
> > + ps2emu->tail += copylen;
> > + if (ps2emu->tail == PS2EMU_BUFSIZE)
> > + ps2emu->tail = 0;
>
> Locking needed gain - you can have several readers.
>
> > +
> > + return copylen;
> > +}
> > +
> > +static ssize_t ps2emu_char_write(struct file *file, const char
> > __user *buffer,
> > + size_t count, loff_t *ppos)
> > +{
> > + struct ps2emu_device *ps2emu = file->private_data;
> > + struct ps2emu_cmd cmd;
> > +
> > + if (count < sizeof(cmd))
> > + return -EINVAL;
> > +
> > + if (copy_from_user(&cmd, buffer, sizeof(cmd)))
> > + return -EFAULT;
> > +
> > + switch (cmd.type) {
> > + case PS2EMU_CMD_REGISTER:
> > + if (!ps2emu->serio.id.type) {
> > + dev_warn(ps2emu_misc.this_device,
> > + "No port type given on
> > /dev/ps2emu\n");
> > +
> > + return -EINVAL;
> > + }
> > + if (ps2emu->running) {
> > + dev_warn(ps2emu_misc.this_device,
> > + "Begin command sent, but we're
> > already running\n");
> > +
> > + return -EINVAL;
> > + }
> > +
> > + ps2emu->running = true;
> > + serio_register_port(&ps2emu->serio);
> > + break;
> > +
> > + case PS2EMU_CMD_SET_PORT_TYPE:
> > + if (ps2emu->running) {
> > + dev_warn(ps2emu_misc.this_device,
> > + "Can't change port type on an
> > already running ps2emu instance\n");
> > +
> > + return -EINVAL;
> > + }
> > +
> > + switch (cmd.data) {
> > + case SERIO_8042:
> > + case SERIO_8042_XL:
> > + case SERIO_PS_PSTHRU:
> > + ps2emu->serio.id.type = cmd.data;
> > + break;
> > +
> > + default:
> > + dev_warn(ps2emu_misc.this_device,
> > + "Invalid port type 0x%hhx\n",
> > cmd.data);
>
> Why not allow others?
I don't have an answer for this and this is something that's crossed my
mind a couple of times. So, we could very much allow for other port
types (although userspace applications would have to be written for
them, since the tricks we do to allow PS/2 recording are specific to
PS/2). If you think this is a good idea I'm definitely not opposed to
it.
Cheers,
Stephen Chandler Paul
> > +
> > + return -EINVAL;
> > + }
> > +
> > + break;
> > +
> > + case PS2EMU_CMD_SEND_INTERRUPT:
> > + if (!ps2emu->running) {
> > + dev_warn(ps2emu_misc.this_device,
> > + "The device must be registered
> > before sending interrupts\n");
> > +
> > + return -EINVAL;
> > + }
> > +
> > + serio_interrupt(&ps2emu->serio, cmd.data, 0);
> > +
> > + break;
> > +
> > + default:
> > + return -EINVAL;
> > + }
> > +
> > + return sizeof(cmd);
> > +}
> > +
> > +static unsigned int ps2emu_char_poll(struct file *file, poll_table
> > *wait)
> > +{
> > + struct ps2emu_device *ps2emu = file->private_data;
> > +
> > + poll_wait(file, &ps2emu->waitq, wait);
> > +
> > + if (ps2emu->head != ps2emu->tail)
> > + return POLLIN | POLLRDNORM;
> > +
> > + return 0;
> > +}
> > +
> > +static const struct file_operations ps2emu_fops = {
> > + .owner = THIS_MODULE,
> > + .open = ps2emu_char_open,
> > + .release = ps2emu_char_release,
> > + .read = ps2emu_char_read,
> > + .write = ps2emu_char_write,
> > + .poll = ps2emu_char_poll,
> > + .llseek = no_llseek,
> > +};
> > +
> > +static struct miscdevice ps2emu_misc = {
> > + .fops = &ps2emu_fops,
> > + .minor = MISC_DYNAMIC_MINOR,
> > + .name = PS2EMU_NAME,
> > +};
> > +
> > +MODULE_AUTHOR("Stephen Chandler Paul <thatslyude@gmail.com>");
> > +MODULE_DESCRIPTION("ps2emu");
> > +MODULE_LICENSE("GPL");
> > +
> > +module_driver(ps2emu_misc, misc_register, misc_deregister);
> > diff --git a/include/uapi/linux/ps2emu.h
> > b/include/uapi/linux/ps2emu.h
> > new file mode 100644
> > index 0000000..63f5cc9
> > --- /dev/null
> > +++ b/include/uapi/linux/ps2emu.h
> > @@ -0,0 +1,42 @@
> > +/*
> > + * ps2emu.h
> > + * Copyright (C) 2015 Red Hat
> > + * Copyright (C) 2015 Lyude (Stephen Chandler Paul) <
> > cpaul@redhat.com>
> > + *
> > + * This program is free software; you can redistribute it and/or
> > modify it
> > + * under the terms of the GNU Lesser General Public License as
> > published by the
> > + * Free Software Foundation; either version 2 of the License, or
> > (at your
> > + * option) any later version.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > but WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of
> > MERCHANTABILITY or FITNESS
> > + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
> > License for more
> > + * details.
> > + *
> > + * This is the public header used for user-space communication
> > with the ps2emu
> > + * driver. __attribute__((__packed__)) is used for all structs to
> > keep ABI
> > + * compatibility between all architectures.
> > + */
> > +
> > +#ifndef _PS2EMU_H
> > +#define _PS2EMU_H
> > +
> > +#include <linux/types.h>
> > +
> > +#define PS2EMU_CMD_REGISTER 0
> > +#define PS2EMU_CMD_SET_PORT_TYPE 1
> > +#define PS2EMU_CMD_SEND_INTERRUPT 2
> > +
> > +/*
> > + * ps2emu Commands
> > + * All commands sent to /dev/ps2emu are encoded using this
> > structure. The type
> > + * field should contain a PS2EMU_CMD* value that indicates what
> > kind of command
> > + * is being sent to ps2emu. The data field should contain the
> > accompanying
> > + * argument for the command, if there is one.
> > + */
> > +struct ps2emu_cmd {
> > + __u8 type;
> > + __u8 data;
> > +} __attribute__((__packed__));
> > +
> > +#endif /* !_PS2EMU_H */
> > --
> > 2.4.3
> >
>
> Thanks.
>
^ permalink raw reply
* Re: [RFC 1/1 v2] Input: Add ps2emu module
From: Dmitry Torokhov @ 2015-07-21 22:13 UTC (permalink / raw)
To: Stephen Chandler Paul
Cc: Andrew Morton, Mauro Carvalho Chehab, Greg KH, Arnd Bergmann,
Joe Perches, Jiri Slaby, Vishnu Patekar, Sebastian Ott, linux-doc,
linux-kernel, linux-input, linux-api, Benjamin Tissoires,
Hans de Goede
In-Reply-To: <1437514937.2925.25.camel@redhat.com>
On Tue, Jul 21, 2015 at 05:42:17PM -0400, Stephen Chandler Paul wrote:
> On Tue, 2015-07-21 at 13:46 -0700, Dmitry Torokhov wrote:
> > Hi Stephen,
> >
> > On Tue, Jul 21, 2015 at 03:47:17PM -0400, Stephen Chandler Paul
> > wrote:
> > > Debugging input devices, specifically laptop touchpads, can be
> > > tricky
> > > without having the physical device handy. Here we try to remedy
> > > that
> > > with ps2emu. This module allows an application to connect to a
> > > character
> > > device provided by the kernel, and simulate any PS/2 device. In
> > > combination with userspace programs that can record PS/2 devices
> > > and
> > > replay them through the /dev/ps2emu device, this allows developers
> > > to
> > > debug driver issues on the PS/2 level with devices simply by
> > > requesting
> > > a recording from the user experiencing the issue without having to
> > > have
> > > the physical hardware in front of them.
> >
> > This does not seem to be limited to PS/2, why not userio to keep it
> > in
> > the vein of uinput, uhid, etc?
> >
> > >
> > > Signed-off-by: Stephen Chandler Paul <cpaul@redhat.com>
> > > Reviewed-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> > > ---
> > > Changes
> > > * Remove PS2EMU_MINOR, use MISC_DYNAMIC_MINOR
> > > * Remove ps2emu_warn(), just use dev_warn()
> > > * Don't return value from copy_to_user(), return -EFAULT
> > > * Remove usages of unlikely()
> > > * Remove call to nonseekable_open()
> > >
> > > Things I didn't change
> > > * Didn't rename this_device, I might have misinterpreted what you
> > > were saying
> > > but this_device is a member of a struct that isn't defined in any
> > > of my own
> > > patches. I could have renamed ps2emu_misc and ps2emu_fops to misc
> > > and fops,
> > > but I'm guessing that's the wrong thing to do if I go off the
> > > style of the
> > > other driver files in the kernel tree (in drivers/input, anyway).
> > >
> > > Documentation/input/ps2emu.txt | 72 ++++++++++++
> > > MAINTAINERS | 6 +
> > > drivers/input/serio/Kconfig | 10 ++
> > > drivers/input/serio/Makefile | 1 +
> > > drivers/input/serio/ps2emu.c | 250
> > > +++++++++++++++++++++++++++++++++++++++++
> > > include/uapi/linux/ps2emu.h | 42 +++++++
> > > 6 files changed, 381 insertions(+)
> > > create mode 100644 Documentation/input/ps2emu.txt
> > > create mode 100644 drivers/input/serio/ps2emu.c
> > > create mode 100644 include/uapi/linux/ps2emu.h
> > >
> > > diff --git a/Documentation/input/ps2emu.txt
> > > b/Documentation/input/ps2emu.txt
> > > new file mode 100644
> > > index 0000000..560298c
> > > --- /dev/null
> > > +++ b/Documentation/input/ps2emu.txt
> > > @@ -0,0 +1,72 @@
> > > + The ps2emu Protocol
> > > + (c) 2015 Stephen Chandler Paul <thatslyude@gmail.com>
> > > + Sponsored by Red Hat
> > > +------------------------------------------------------------------
> > > --------------
> > > +
> > > +1. Introduction
> > > +~~~~~~~~~~~~~~~
> > > + This module is intended to try to make the lives of input driver
> > > developers
> > > +easier by allowing them to test various PS/2 devices (mainly the
> > > various
> > > +touchpads found on laptops) without having to have the physical
> > > device in front
> > > +of them. ps2emu accomplishes this by allowing any privileged
> > > userspace program
> > > +to directly interact with the kernel's serio driver and pretend to
> > > be a PS/2
> > > +device.
> > > +
> > > +2. Usage overview
> > > +~~~~~~~~~~~~~~~~~
> > > + In order to interact with the ps2emu kernel module, one simply
> > > opens the
> > > +/dev/ps2emu character device in their applications. Commands are
> > > sent to the
> > > +kernel module by writing to the device, and any data received from
> > > the serio
> > > +driver is read as-is from the /dev/ps2emu device. All of the
> > > structures and
> > > +macros you need to interact with the device are defined in
> > > <linux/ps2emu.h>.
> > > +
> > > +3. Command Structure
> > > +~~~~~~~~~~~~~~~~~~~~
> > > + The struct used for sending commands to /dev/ps2emu is as
> > > follows:
> > > +
> > > + struct ps2emu_cmd {
> > > + __u8 type;
> > > + __u8 data;
> > > + };
> > > +
> > > + "type" describes the type of command that is being sent. This
> > > can be any one
> > > +of the PS2EMU_CMD macros defined in <linux/ps2emu.h>. "data" is
> > > the argument
> > > +that goes along with the command. In the event that the command
> > > doesn't have an
> > > +argument, this field can be left untouched and will be ignored by
> > > the kernel.
> > > +Each command should be sent by writing the struct directly to the
> > > character
> > > +device. In the event that the command you send is invalid, an
> > > error will be
> > > +returned by the character device and a more descriptive error will
> > > be printed
> > > +to the kernel log. Only one command can be sent at a time, any
> > > additional data
> > > +written to the character device after the initial command will be
> > > ignored.
> > > + To close the virtual PS/2 port, just close /dev/ps2emu.
> > > +
> > > +4. Commands
> > > +~~~~~~~~~~~
> > > +
> > > +4.1 PS2EMU_CMD_REGISTER
> > > +~~~~~~~~~~~~~~~~~~~~~~~
> > > + Registers the port with the serio driver and begins transmitting
> > > data back and
> > > +forth. Registration can only be performed once a port type is set
> > > with
> > > +PS2EMU_CMD_SET_PORT_TYPE. Has no argument.
> > > +
> > > +4.2 PS2EMU_CMD_SET_PORT_TYPE
> > > +~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> > > + Sets the type of port we're emulating, where "data" is the port
> > > type being
> > > +set. Can be any of the following macros from <linux/serio.h>:
> > > +
> > > + SERIO_8042
> > > + SERIO_8042_XL
> > > + SERIO_PS_PSTHRU
> >
> > Why not any others?
> >
> > > +
> > > +4.3 PS2EMU_CMD_SEND_INTERRUPT
> > > +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> > > + Sends an interrupt through the virtual PS/2 port to the serio
> > > driver, where
> > > +"data" is the interrupt data being sent.
> >
> > Might want to also allow sending "flags".
> I'm eventually planning on doing this, I can add something in the
> current version if you want but I didn't see any immediate need for
> them.
Hmm, I guess we can do it later.
> >
> > > +
> > > +5. Userspace tools
> > > +~~~~~~~~~~~~~~~~~~
> > > + The ps2emu userspace tools are able to record PS/2 devices using
> > > some of the
> > > +debugging information from i8042, and play back the devices on
> > > /dev/ps2emu. The
> > > +latest version of these tools can be found at:
> > > +
> > > + https://github.com/Lyude/ps2emu
> > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > index a226416..68a0977 100644
> > > --- a/MAINTAINERS
> > > +++ b/MAINTAINERS
> > > @@ -10877,6 +10877,12 @@ S: Maintained
> > > F: drivers/media/v4l2-core/videobuf2-*
> > > F: include/media/videobuf2-*
> > >
> > > +VIRTUAL PS/2 DEVICE DRIVER
> > > +M: Stephen Chandler Paul <thatslyude@gmail.com>
> > > +S: Maintained
> > > +F: drivers/input/serio/ps2emu.c
> > > +F: include/uapi/linux/ps2emu.h
> > > +
> > > VIRTIO CONSOLE DRIVER
> > > M: Amit Shah <amit.shah@redhat.com>
> > > L: virtualization@lists.linux-foundation.org
> > > diff --git a/drivers/input/serio/Kconfig
> > > b/drivers/input/serio/Kconfig
> > > index 200841b..cc3563f 100644
> > > --- a/drivers/input/serio/Kconfig
> > > +++ b/drivers/input/serio/Kconfig
> > > @@ -292,4 +292,14 @@ config SERIO_SUN4I_PS2
> > > To compile this driver as a module, choose M here: the
> > > module will be called sun4i-ps2.
> > >
> > > +config PS2EMU
> > > + tristate "Virtual PS/2 device support"
> > > + help
> > > + Say Y here if you want to emulate PS/2 devices using the
> > > ps2emu tools.
> > > +
> > > + To compile this driver as a module, choose M here: the
> > > module will be
> > > + called ps2emu.
> > > +
> > > + If you are unsure, say N.
> > > +
> > > endif
> > > diff --git a/drivers/input/serio/Makefile
> > > b/drivers/input/serio/Makefile
> > > index c600089..7b20936 100644
> > > --- a/drivers/input/serio/Makefile
> > > +++ b/drivers/input/serio/Makefile
> > > @@ -30,3 +30,4 @@ obj-$(CONFIG_SERIO_APBPS2) += apbps2.o
> > > obj-$(CONFIG_SERIO_OLPC_APSP) += olpc_apsp.o
> > > obj-$(CONFIG_HYPERV_KEYBOARD) += hyperv-keyboard.o
> > > obj-$(CONFIG_SERIO_SUN4I_PS2) += sun4i-ps2.o
> > > +obj-$(CONFIG_PS2EMU) += ps2emu.o
> > > diff --git a/drivers/input/serio/ps2emu.c
> > > b/drivers/input/serio/ps2emu.c
> > > new file mode 100644
> > > index 0000000..73bf389
> > > --- /dev/null
> > > +++ b/drivers/input/serio/ps2emu.c
> > > @@ -0,0 +1,250 @@
> > > +/*
> > > + * ps2emu kernel PS/2 device emulation module
> > > + * Copyright (C) 2015 Red Hat
> > > + * Copyright (C) 2015 Stephen Chandler Paul <thatslyude@gmail.com>
> > > + *
> > > + * This program is free software; you can redistribute it and/or
> > > modify it
> > > + * under the terms of the GNU Lesser General Public License as
> > > published by the
> > > + * Free Software Foundation; either version 2 of the License, or
> > > (at your
> > > + * option) any later version.
> > > + *
> > > + * This program is distributed in the hope that it will be useful,
> > > but WITHOUT
> > > + * ANY WARRANTY; without even the implied warranty of
> > > MERCHANTABILITY or FITNESS
> > > + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
> > > License for more
> > > + * details.
> > > + */
> > > +#include <linux/circ_buf.h>
> > > +#include <linux/mutex.h>
> > > +#include <linux/module.h>
> > > +#include <linux/init.h>
> > > +#include <linux/kernel.h>
> > > +#include <linux/serio.h>
> > > +#include <linux/libps2.h>
> > > +#include <linux/slab.h>
> > > +#include <linux/fs.h>
> > > +#include <linux/miscdevice.h>
> > > +#include <linux/sched.h>
> > > +#include <linux/poll.h>
> > > +#include <uapi/linux/ps2emu.h>
> > > +
> > > +#define PS2EMU_NAME "ps2emu"
> > > +#define PS2EMU_BUFSIZE 16
> > > +
> > > +static const struct file_operations ps2emu_fops;
> > > +static struct miscdevice ps2emu_misc;
> > > +
> > > +struct ps2emu_device {
> > > + struct serio serio;
> >
> > Do not embed serio into your structire but allocate separately as
> > serio
> > is refcounted and you do not have exact control over when it will be
> > released.
> >
> > > +
> > > + bool running;
> > > +
> > > + u8 head;
> > > + u8 tail;
> > > + unsigned char buf[PS2EMU_BUFSIZE];
> > > +
> > > + wait_queue_head_t waitq;
> > > +};
> > > +
> > > +/**
> > > + * ps2emu_device_write - Write data from serio to a ps2emu device
> > > in userspace
> > > + * @id: The serio port for the ps2emu device
> > > + * @val: The data to write to the device
> > > + */
> > > +static int ps2emu_device_write(struct serio *id, unsigned char
> > > val)
> > > +{
> > > + struct ps2emu_device *ps2emu = id->port_data;
> > > + u8 newhead;
> > > +
> > > + ps2emu->buf[ps2emu->head] = val;
> > > +
> > > + newhead = ps2emu->head + 1;
> > > +
> > > + if (newhead < PS2EMU_BUFSIZE)
> > > + ps2emu->head = newhead;
> > > + else
> > > + ps2emu->head = 0;
> >
> > Why not
> >
> > ps2emu->head = (ps2emu->head + 1) % PS2EMU_BUFSIZE;
>
> > given your chosen bufsize it shoudl be optimized to increment and
> > bitwise and.
> >
> > You need locking though.
> >
> > > +
> > > + if (newhead == ps2emu->tail)
> > > + dev_warn(ps2emu_misc.this_device,
> > > + "Buffer overflowed, ps2emu client isn't
> > > keeping up");
> > > +
> > > + wake_up_interruptible(&ps2emu->waitq);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int ps2emu_char_open(struct inode *inode, struct file
> > > *file)
> > > +{
> > > + struct ps2emu_device *ps2emu = NULL;
> > > +
> > > + ps2emu = kzalloc(sizeof(struct ps2emu_device),
> > > GFP_KERNEL);
> > > + if (!ps2emu)
> > > + return -ENOMEM;
> > > +
> > > + init_waitqueue_head(&ps2emu->waitq);
> > > +
> > > + ps2emu->serio.write = ps2emu_device_write;
> > > + ps2emu->serio.port_data = ps2emu;
> > > +
> > > + file->private_data = ps2emu;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int ps2emu_char_release(struct inode *inode, struct file
> > > *file)
> > > +{
> > > + struct ps2emu_device *ps2emu = file->private_data;
> > > +
> > > + /*
> > > + * We can rely on serio_unregister_port() to free the
> > > ps2emu struct on
> > > + * it's own
> > > + */
> > > + if (ps2emu->running)
> > > + serio_unregister_port(&ps2emu->serio);
> > > + else
> > > + kfree(ps2emu);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static ssize_t ps2emu_char_read(struct file *file, char __user
> > > *buffer,
> > > + size_t count, loff_t *ppos)
> > > +{
> > > + struct ps2emu_device *ps2emu = file->private_data;
> > > + int ret;
> > > + size_t nonwrap_len, copylen;
> > > + u8 head; /* So we only access ps2emu->head once */
> >
> > Why is it important?
>
> Benjamin had mentioned that this comment might need clarification and
> it looks like he was right. So, originally I did have locking but I did
> some reading on Documentation/circular-buffers.txt and noticed that
> there's a couple of mentions of locking not needed and I'm pretty sure
> this is a situation where this is actually the case (although I should
> be using ACCESS_ONCE() there).
This only works if there is exactly one reader and writer, but we can
have many threads executing ps2emu_device_write() and similarly many
threads executing ps2emu_char_read(). When you do operations like:
<consume data from buffer>
tail += increment;
<adjust tail>
...
and have many consumers your tail will end up being somewhat random
value.
That is why you do need locking. The kernel needs to keep it's own
internal state consistent and race free even though there is unlikely
userspace consumer actually using multiple threads on the same fd
descriptor with this kind of device.
> So, there will be multiple readers. In this code however, there will
> never be multiple writers. The only one ever writing to the head is
> ps2emu_device_write(), and the only one ever writing to the tail is
> ps2emu_char_read().
> During run time, we can always expect the head to
> move forward or wrap around. Going by this logic, if the head moves
> forward while we're reading it, the worst that will happen is that
> there will now be some data in the buffer that we won't check until we
> finish up in ps2emu_char_read(). This being said, in order to be safe
> we try to read from ps2emu->head only once and then work with the last
> value we retrieved from there for the duration of the function, so that
> the head position we're going off of in the function doesn't change
> while we're using it.
> All of this being said, if the head does happen to catch up to the tail
> and not the other way around, data will be lost (in my testing though
> this really doesn't happen at all during normal run time since most PS2
> commands sequences don't go over 16 bytes). But all that means is that
> the userspace application will go out of sync and not be able to
> continue (which would happen anyway, since the driver can timeout
> waiting for a response).
>
> Note that I might not have explained that very well, this is the first
> time I've done something like this. I don't think this behavior is bad,
> but I'm new to writing drivers so I may very well be wrong :).
>
> > > +
> > > + if (file->f_flags & O_NONBLOCK) {
> > > + head = ps2emu->head;
> > > +
> > > + if (head == ps2emu->tail)
> > > + return -EAGAIN;
> > > + } else {
> > > + ret = wait_event_interruptible(
> > > + ps2emu->waitq, (head = ps2emu->head) !=
> > > ps2emu->tail);
> >
> > If I understand it correctly you need to treat blocking read with 0
> > length properly: it should not wait.
> >
> > > +
> > > + if (ret)
> > > + return ret;
> > > + }
> > > +
> > > + nonwrap_len = CIRC_CNT_TO_END(head, ps2emu->tail,
> > > PS2EMU_BUFSIZE);
> > > + copylen = min(nonwrap_len, count);
> > > +
> > > + if (copy_to_user(buffer, &ps2emu->buf[ps2emu->tail],
> > > copylen))
> > > + return -EFAULT;
> > > +
> > > + ps2emu->tail += copylen;
> > > + if (ps2emu->tail == PS2EMU_BUFSIZE)
> > > + ps2emu->tail = 0;
> >
> > Locking needed gain - you can have several readers.
> >
> > > +
> > > + return copylen;
> > > +}
> > > +
> > > +static ssize_t ps2emu_char_write(struct file *file, const char
> > > __user *buffer,
> > > + size_t count, loff_t *ppos)
> > > +{
> > > + struct ps2emu_device *ps2emu = file->private_data;
> > > + struct ps2emu_cmd cmd;
> > > +
> > > + if (count < sizeof(cmd))
> > > + return -EINVAL;
> > > +
> > > + if (copy_from_user(&cmd, buffer, sizeof(cmd)))
> > > + return -EFAULT;
> > > +
> > > + switch (cmd.type) {
> > > + case PS2EMU_CMD_REGISTER:
> > > + if (!ps2emu->serio.id.type) {
> > > + dev_warn(ps2emu_misc.this_device,
> > > + "No port type given on
> > > /dev/ps2emu\n");
> > > +
> > > + return -EINVAL;
> > > + }
> > > + if (ps2emu->running) {
> > > + dev_warn(ps2emu_misc.this_device,
> > > + "Begin command sent, but we're
> > > already running\n");
> > > +
> > > + return -EINVAL;
> > > + }
> > > +
> > > + ps2emu->running = true;
> > > + serio_register_port(&ps2emu->serio);
> > > + break;
> > > +
> > > + case PS2EMU_CMD_SET_PORT_TYPE:
> > > + if (ps2emu->running) {
> > > + dev_warn(ps2emu_misc.this_device,
> > > + "Can't change port type on an
> > > already running ps2emu instance\n");
> > > +
> > > + return -EINVAL;
> > > + }
> > > +
> > > + switch (cmd.data) {
> > > + case SERIO_8042:
> > > + case SERIO_8042_XL:
> > > + case SERIO_PS_PSTHRU:
> > > + ps2emu->serio.id.type = cmd.data;
> > > + break;
> > > +
> > > + default:
> > > + dev_warn(ps2emu_misc.this_device,
> > > + "Invalid port type 0x%hhx\n",
> > > cmd.data);
> >
> > Why not allow others?
> I don't have an answer for this and this is something that's crossed my
> mind a couple of times. So, we could very much allow for other port
> types (although userspace applications would have to be written for
> them, since the tricks we do to allow PS/2 recording are specific to
> PS/2). If you think this is a good idea I'm definitely not opposed to
> it.
Yes, I'd just allow creating serio of any type, there is no benefit in
limiting it.
Thanks.
--
Dmitry
^ permalink raw reply
* Re: [PATCH -mm v9 0/8] idle memory tracking
From: Andrew Morton @ 2015-07-21 23:34 UTC (permalink / raw)
To: Vladimir Davydov
Cc: Andres Lagar-Cavilla, Minchan Kim, Raghavendra K T,
Johannes Weiner, Michal Hocko, Greg Thelen, Michel Lespinasse,
David Rientjes, Pavel Emelyanov, Cyrill Gorcunov, Jonathan Corbet,
linux-api, linux-doc, linux-mm, cgroups, linux-kernel, Kees Cook
In-Reply-To: <cover.1437303956.git.vdavydov@parallels.com>
On Sun, 19 Jul 2015 15:31:09 +0300 Vladimir Davydov <vdavydov@parallels.com> wrote:
> Hi,
>
> This patch set introduces a new user API for tracking user memory pages
> that have not been used for a given period of time. The purpose of this
> is to provide the userspace with the means of tracking a workload's
> working set, i.e. the set of pages that are actively used by the
> workload. Knowing the working set size can be useful for partitioning
> the system more efficiently, e.g. by tuning memory cgroup limits
> appropriately, or for job placement within a compute cluster.
>
> It is based on top of v4.2-rc2-mmotm-2015-07-15-16-46
> It applies without conflicts to v4.2-rc2-mmotm-2015-07-17-16-04 as well
>
> ---- USE CASES ----
>
> The unified cgroup hierarchy has memory.low and memory.high knobs, which
> are defined as the low and high boundaries for the workload working set
> size. However, the working set size of a workload may be unknown or
> change in time. With this patch set, one can periodically estimate the
> amount of memory unused by each cgroup and tune their memory.low and
> memory.high parameters accordingly, therefore optimizing the overall
> memory utilization.
>
> Another use case is balancing workloads within a compute cluster.
> Knowing how much memory is not really used by a workload unit may help
> take a more optimal decision when considering migrating the unit to
> another node within the cluster.
>
> Also, as noted by Minchan, this would be useful for per-process reclaim
> (https://lwn.net/Articles/545668/). With idle tracking, we could reclaim idle
> pages only by smart user memory manager.
>
> ---- USER API ----
>
> The user API consists of two new proc files:
>
> * /proc/kpageidle. This file implements a bitmap where each bit corresponds
> to a page, indexed by PFN.
What are the bit mappings? If I read the first byte of /proc/kpageidle
I get PFN #0 in bit zero of that byte? And the second byte of
/proc/kpageidle contains PFN #8 in its LSB, etc?
Maybe this is covered in the documentation file.
> When the bit is set, the corresponding page is
> idle. A page is considered idle if it has not been accessed since it was
> marked idle.
Perhaps we can spell out in some detail what "accessed" means? I see
you've hooked into mark_page_accessed(), so a read from disk is an
access. What about a write to disk? And what about a page being
accessed from some random device (could hook into get_user_pages()?) Is
getting written to swap an access? When a dirty pagecache page is
written out by kswapd or direct reclaim?
This also should be in the permanent documentation.
> To mark a page idle one should set the bit corresponding to the
> page by writing to the file. A value written to the file is OR-ed with the
> current bitmap value. Only user memory pages can be marked idle, for other
> page types input is silently ignored. Writing to this file beyond max PFN
> results in the ENXIO error. Only available when CONFIG_IDLE_PAGE_TRACKING is
> set.
>
> This file can be used to estimate the amount of pages that are not
> used by a particular workload as follows:
>
> 1. mark all pages of interest idle by setting corresponding bits in the
> /proc/kpageidle bitmap
> 2. wait until the workload accesses its working set
> 3. read /proc/kpageidle and count the number of bits set
Security implications. This interface could be used to learn about a
sensitive application by poking data at it and then observing its
memory access patterns. Perhaps this is why the proc files are
root-only (whcih I assume is sufficient). Some words here about the
security side of things and the reasoning behind the chosen permissions
would be good to have.
> * /proc/kpagecgroup. This file contains a 64-bit inode number of the
> memory cgroup each page is charged to, indexed by PFN.
Actually "closest online ancestor". This also should be in the
interface documentation.
> Only available when CONFIG_MEMCG is set.
CONFIG_MEMCG and CONFIG_IDLE_PAGE_TRACKING I assume?
>
> This file can be used to find all pages (including unmapped file
> pages) accounted to a particular cgroup. Using /proc/kpageidle, one
> can then estimate the cgroup working set size.
>
> For an example of using these files for estimating the amount of unused
> memory pages per each memory cgroup, please see the script attached
> below.
Why were these put in /proc anyway? Rather than under /sys/fs/cgroup
somewhere? Presumably because /proc/kpageidle is useful in non-memcg
setups.
> ---- PERFORMANCE EVALUATION ----
"^___" means "end of changelog". Perhaps that should have been
"^---\n" - unclear.
> Documentation/vm/pagemap.txt | 22 ++-
I think we'll need quite a lot more than this to fully describe the
interface?
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [PATCH -mm v9 1/8] memcg: add page_cgroup_ino helper
From: Andrew Morton @ 2015-07-21 23:34 UTC (permalink / raw)
To: Vladimir Davydov
Cc: Andres Lagar-Cavilla, Minchan Kim, Raghavendra K T,
Johannes Weiner, Michal Hocko, Greg Thelen, Michel Lespinasse,
David Rientjes, Pavel Emelyanov, Cyrill Gorcunov, Jonathan Corbet,
linux-api, linux-doc, linux-mm, cgroups, linux-kernel
In-Reply-To: <aa0190b76489260b4d1b65cdfa65221f4e6390f5.1437303956.git.vdavydov@parallels.com>
On Sun, 19 Jul 2015 15:31:10 +0300 Vladimir Davydov <vdavydov@parallels.com> wrote:
> This function returns the inode number of the closest online ancestor of
> the memory cgroup a page is charged to. It is required for exporting
> information about which page is charged to which cgroup to userspace,
> which will be introduced by a following patch.
>
> ...
>
> --- a/mm/memcontrol.c
> +++ b/mm/memcontrol.c
> @@ -441,6 +441,29 @@ struct cgroup_subsys_state *mem_cgroup_css_from_page(struct page *page)
> return &memcg->css;
> }
>
> +/**
> + * page_cgroup_ino - return inode number of the memcg a page is charged to
> + * @page: the page
> + *
> + * Look up the closest online ancestor of the memory cgroup @page is charged to
> + * and return its inode number or 0 if @page is not charged to any cgroup. It
> + * is safe to call this function without holding a reference to @page.
> + */
> +unsigned long page_cgroup_ino(struct page *page)
Shouldn't it return an ino_t?
> +{
> + struct mem_cgroup *memcg;
> + unsigned long ino = 0;
> +
> + rcu_read_lock();
> + memcg = READ_ONCE(page->mem_cgroup);
> + while (memcg && !(memcg->css.flags & CSS_ONLINE))
> + memcg = parent_mem_cgroup(memcg);
> + if (memcg)
> + ino = cgroup_ino(memcg->css.cgroup);
> + rcu_read_unlock();
> + return ino;
> +}
The function is racy, isn't it? There's nothing to prevent this inode
from getting torn down and potentially reallocated one nanosecond after
page_cgroup_ino() returns? If so, it is only safely usable by things
which don't care (such as procfs interfaces) and this should be
documented in some fashion.
^ permalink raw reply
* Re: [PATCH -mm v9 2/8] hwpoison: use page_cgroup_ino for filtering by memcg
From: Andrew Morton @ 2015-07-21 23:34 UTC (permalink / raw)
To: Vladimir Davydov
Cc: Andres Lagar-Cavilla, Minchan Kim, Raghavendra K T,
Johannes Weiner, Michal Hocko, Greg Thelen, Michel Lespinasse,
David Rientjes, Pavel Emelyanov, Cyrill Gorcunov, Jonathan Corbet,
linux-api, linux-doc, linux-mm, cgroups, linux-kernel
In-Reply-To: <94215634d13582d2a1453686d6cc6b1a59b07d2a.1437303956.git.vdavydov@parallels.com>
On Sun, 19 Jul 2015 15:31:11 +0300 Vladimir Davydov <vdavydov@parallels.com> wrote:
> Hwpoison allows to filter pages by memory cgroup ino. Currently, it
> calls try_get_mem_cgroup_from_page to obtain the cgroup from a page and
> then its ino using cgroup_ino, but now we have an apter method for that,
> page_cgroup_ino, so use it instead.
I assume "an apter" was supposed to be "a helper"?
> --- a/mm/hwpoison-inject.c
> +++ b/mm/hwpoison-inject.c
> @@ -45,12 +45,9 @@ static int hwpoison_inject(void *data, u64 val)
> /*
> * do a racy check with elevated page count, to make sure PG_hwpoison
> * will only be set for the targeted owner (or on a free page).
> - * We temporarily take page lock for try_get_mem_cgroup_from_page().
> * memory_failure() will redo the check reliably inside page lock.
> */
> - lock_page(hpage);
> err = hwpoison_filter(hpage);
> - unlock_page(hpage);
> if (err)
> goto put_out;
>
> @@ -126,7 +123,7 @@ static int pfn_inject_init(void)
> if (!dentry)
> goto fail;
>
> -#ifdef CONFIG_MEMCG_SWAP
> +#ifdef CONFIG_MEMCG
> dentry = debugfs_create_u64("corrupt-filter-memcg", 0600,
> hwpoison_dir, &hwpoison_filter_memcg);
> if (!dentry)
Confused. We're changing the conditions under which this debugfs file
is created. Is this a typo or some unchangelogged thing or what?
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [PATCH -mm v9 4/8] proc: add kpagecgroup file
From: Andrew Morton @ 2015-07-21 23:34 UTC (permalink / raw)
To: Vladimir Davydov
Cc: Andres Lagar-Cavilla, Minchan Kim, Raghavendra K T,
Johannes Weiner, Michal Hocko, Greg Thelen, Michel Lespinasse,
David Rientjes, Pavel Emelyanov, Cyrill Gorcunov, Jonathan Corbet,
linux-api, linux-doc, linux-mm, cgroups, linux-kernel
In-Reply-To: <679498f8d3f87c1ee57b7c3b58382193c9046b6a.1437303956.git.vdavydov@parallels.com>
On Sun, 19 Jul 2015 15:31:13 +0300 Vladimir Davydov <vdavydov@parallels.com> wrote:
> /proc/kpagecgroup contains a 64-bit inode number of the memory cgroup
> each page is charged to, indexed by PFN. Having this information is
> useful for estimating a cgroup working set size.
>
> The file is present if CONFIG_PROC_PAGE_MONITOR && CONFIG_MEMCG.
>
> ...
>
> @@ -225,10 +226,62 @@ static const struct file_operations proc_kpageflags_operations = {
> .read = kpageflags_read,
> };
>
> +#ifdef CONFIG_MEMCG
> +static ssize_t kpagecgroup_read(struct file *file, char __user *buf,
> + size_t count, loff_t *ppos)
> +{
> + u64 __user *out = (u64 __user *)buf;
> + struct page *ppage;
> + unsigned long src = *ppos;
> + unsigned long pfn;
> + ssize_t ret = 0;
> + u64 ino;
> +
> + pfn = src / KPMSIZE;
> + count = min_t(unsigned long, count, (max_pfn * KPMSIZE) - src);
> + if (src & KPMMASK || count & KPMMASK)
> + return -EINVAL;
The user-facing documentation should explain that reads must be
performed in multiple-of-8 sizes.
> + while (count > 0) {
> + if (pfn_valid(pfn))
> + ppage = pfn_to_page(pfn);
> + else
> + ppage = NULL;
> +
> + if (ppage)
> + ino = page_cgroup_ino(ppage);
> + else
> + ino = 0;
> +
> + if (put_user(ino, out)) {
> + ret = -EFAULT;
Here we do the usual procfs violation of read() behaviour. read()
normally only returns an error if it read nothing. This code will
transfer a megabyte then return -EFAULT so userspace doesn't know that
it got that megabyte.
That's easy to fix, but procfs files do this all over the place anyway :(
> + break;
> + }
> +
> + pfn++;
> + out++;
> + count -= KPMSIZE;
> + }
> +
> + *ppos += (char __user *)out - buf;
> + if (!ret)
> + ret = (char __user *)out - buf;
> + return ret;
> +}
> +
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [PATCH -mm v9 6/8] proc: add kpageidle file
From: Andrew Morton @ 2015-07-21 23:34 UTC (permalink / raw)
To: Vladimir Davydov
Cc: Andres Lagar-Cavilla, Minchan Kim, Raghavendra K T,
Johannes Weiner, Michal Hocko, Greg Thelen, Michel Lespinasse,
David Rientjes, Pavel Emelyanov, Cyrill Gorcunov, Jonathan Corbet,
linux-api, linux-doc, linux-mm, cgroups, linux-kernel
In-Reply-To: <d7a78b72053cf529c0c9ff6cbc02ffbb3d58fe35.1437303956.git.vdavydov@parallels.com>
On Sun, 19 Jul 2015 15:31:15 +0300 Vladimir Davydov <vdavydov@parallels.com> wrote:
> Knowing the portion of memory that is not used by a certain application
> or memory cgroup (idle memory) can be useful for partitioning the system
> efficiently, e.g. by setting memory cgroup limits appropriately.
> Currently, the only means to estimate the amount of idle memory provided
> by the kernel is /proc/PID/{clear_refs,smaps}: the user can clear the
> access bit for all pages mapped to a particular process by writing 1 to
> clear_refs, wait for some time, and then count smaps:Referenced.
> However, this method has two serious shortcomings:
>
> - it does not count unmapped file pages
> - it affects the reclaimer logic
>
> To overcome these drawbacks, this patch introduces two new page flags,
> Idle and Young, and a new proc file, /proc/kpageidle. A page's Idle flag
> can only be set from userspace by setting bit in /proc/kpageidle at the
> offset corresponding to the page, and it is cleared whenever the page is
> accessed either through page tables (it is cleared in page_referenced()
> in this case) or using the read(2) system call (mark_page_accessed()).
> Thus by setting the Idle flag for pages of a particular workload, which
> can be found e.g. by reading /proc/PID/pagemap, waiting for some time to
> let the workload access its working set, and then reading the kpageidle
> file, one can estimate the amount of pages that are not used by the
> workload.
>
> The Young page flag is used to avoid interference with the memory
> reclaimer. A page's Young flag is set whenever the Access bit of a page
> table entry pointing to the page is cleared by writing to kpageidle. If
> page_referenced() is called on a Young page, it will add 1 to its return
> value, therefore concealing the fact that the Access bit was cleared.
>
> Note, since there is no room for extra page flags on 32 bit, this
> feature uses extended page flags when compiled on 32 bit.
>
> ...
>
>
> ...
>
> +static void kpageidle_clear_pte_refs(struct page *page)
> +{
> + struct rmap_walk_control rwc = {
> + .rmap_one = kpageidle_clear_pte_refs_one,
> + .anon_lock = page_lock_anon_vma_read,
> + };
I think this can be static const, since `arg' is unused? That would
save some cycles and stack.
> + bool need_lock;
> +
> + if (!page_mapped(page) ||
> + !page_rmapping(page))
> + return;
> +
> + need_lock = !PageAnon(page) || PageKsm(page);
> + if (need_lock && !trylock_page(page))
Oh. So the feature is a bit unreliable.
I'm not immediately seeing anything which would prevent us from using
plain old lock_page() here. What's going on?
> + return;
> +
> + rmap_walk(page, &rwc);
> +
> + if (need_lock)
> + unlock_page(page);
> +}
> +
> +static ssize_t kpageidle_read(struct file *file, char __user *buf,
> + size_t count, loff_t *ppos)
> +{
> + u64 __user *out = (u64 __user *)buf;
> + struct page *page;
> + unsigned long pfn, end_pfn;
> + ssize_t ret = 0;
> + u64 idle_bitmap = 0;
> + int bit;
> +
> + if (*ppos & KPMMASK || count & KPMMASK)
> + return -EINVAL;
Interface requires 8-byte aligned offset and size.
> + pfn = *ppos * BITS_PER_BYTE;
> + if (pfn >= max_pfn)
> + return 0;
> +
> + end_pfn = pfn + count * BITS_PER_BYTE;
> + if (end_pfn > max_pfn)
> + end_pfn = ALIGN(max_pfn, KPMBITS);
So we lose up to 63 pages. Presumably max_pfn is well enough aligned
for this to not matter, dunno.
> + for (; pfn < end_pfn; pfn++) {
> + bit = pfn % KPMBITS;
> + page = kpageidle_get_page(pfn);
> + if (page) {
> + if (page_is_idle(page)) {
> + /*
> + * The page might have been referenced via a
> + * pte, in which case it is not idle. Clear
> + * refs and recheck.
> + */
> + kpageidle_clear_pte_refs(page);
> + if (page_is_idle(page))
> + idle_bitmap |= 1ULL << bit;
I don't understand what's going on here. More details, please?
> + }
> + put_page(page);
> + }
> + if (bit == KPMBITS - 1) {
> + if (put_user(idle_bitmap, out)) {
> + ret = -EFAULT;
> + break;
> + }
> + idle_bitmap = 0;
> + out++;
> + }
> + }
> +
> + *ppos += (char __user *)out - buf;
> + if (!ret)
> + ret = (char __user *)out - buf;
> + return ret;
> +}
> +
> +static ssize_t kpageidle_write(struct file *file, const char __user *buf,
> + size_t count, loff_t *ppos)
> +{
> + const u64 __user *in = (const u64 __user *)buf;
> + struct page *page;
> + unsigned long pfn, end_pfn;
> + ssize_t ret = 0;
> + u64 idle_bitmap = 0;
> + int bit;
> +
> + if (*ppos & KPMMASK || count & KPMMASK)
> + return -EINVAL;
> +
> + pfn = *ppos * BITS_PER_BYTE;
> + if (pfn >= max_pfn)
> + return -ENXIO;
> +
> + end_pfn = pfn + count * BITS_PER_BYTE;
> + if (end_pfn > max_pfn)
> + end_pfn = ALIGN(max_pfn, KPMBITS);
> +
> + for (; pfn < end_pfn; pfn++) {
> + bit = pfn % KPMBITS;
> + if (bit == 0) {
> + if (get_user(idle_bitmap, in)) {
> + ret = -EFAULT;
> + break;
> + }
> + in++;
> + }
> + if (idle_bitmap >> bit & 1) {
Hate it when I have to go look up a C precedence table. This is
if ((idle_bitmap >> bit) & 1) {
> + page = kpageidle_get_page(pfn);
> + if (page) {
> + kpageidle_clear_pte_refs(page);
> + set_page_idle(page);
> + put_page(page);
> + }
> + }
> + }
> +
> + *ppos += (const char __user *)in - buf;
> + if (!ret)
> + ret = (const char __user *)in - buf;
> + return ret;
> +}
> +
>
> ...
>
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [PATCH -mm v9 7/8] proc: export idle flag via kpageflags
From: Andrew Morton @ 2015-07-21 23:35 UTC (permalink / raw)
To: Vladimir Davydov
Cc: Andres Lagar-Cavilla, Minchan Kim, Raghavendra K T,
Johannes Weiner, Michal Hocko, Greg Thelen, Michel Lespinasse,
David Rientjes, Pavel Emelyanov, Cyrill Gorcunov, Jonathan Corbet,
linux-api, linux-doc, linux-mm, cgroups, linux-kernel
In-Reply-To: <4c1eb396150ee14d7c3abf1a6f36ec8cc9dd9435.1437303956.git.vdavydov@parallels.com>
On Sun, 19 Jul 2015 15:31:16 +0300 Vladimir Davydov <vdavydov@parallels.com> wrote:
> As noted by Minchan, a benefit of reading idle flag from
> /proc/kpageflags is that one can easily filter dirty and/or unevictable
> pages while estimating the size of unused memory.
>
> Note that idle flag read from /proc/kpageflags may be stale in case the
> page was accessed via a PTE, because it would be too costly to iterate
> over all page mappings on each /proc/kpageflags read to provide an
> up-to-date value. To make sure the flag is up-to-date one has to read
> /proc/kpageidle first.
Is there any value in teaching the regular old page scanner to update
these flags? If it's doing an rmap scan anyway...
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [PATCH V4 2/6] mm: mlock: Add new mlock, munlock, and munlockall system calls
From: Michael Ellerman @ 2015-07-22 1:25 UTC (permalink / raw)
To: Andrew Morton
Cc: Eric B Munson, linux-mips, linux-m68k, linux-ia64, linux-sh,
Catalin Marinas, Heiko Carstens, Michal Hocko, linux-mm,
sparclinux, linux-arch, Stephen Rothwell, linux-am33-list,
Geert Uytterhoeven, Vlastimil Babka, Guenter Roeck, linux-xtensa,
linux-s390, adi-buildroot-devel, linux-arm-kernel,
linux-cris-kernel, linux-parisc, linux-api, linux-kernel,
linux-alpha, linuxppc-dev
In-Reply-To: <20150721134441.d69e4e1099bd43e56835b3c5@linux-foundation.org>
On Tue, 2015-07-21 at 13:44 -0700, Andrew Morton wrote:
> On Tue, 21 Jul 2015 15:59:37 -0400 Eric B Munson <emunson@akamai.com> wrote:
>
> > With the refactored mlock code, introduce new system calls for mlock,
> > munlock, and munlockall. The new calls will allow the user to specify
> > what lock states are being added or cleared. mlock2 and munlock2 are
> > trivial at the moment, but a follow on patch will add a new mlock state
> > making them useful.
> >
> > munlock2 addresses a limitation of the current implementation. If a
> > user calls mlockall(MCL_CURRENT | MCL_FUTURE) and then later decides
> > that MCL_FUTURE should be removed, they would have to call munlockall()
> > followed by mlockall(MCL_CURRENT) which could potentially be very
> > expensive. The new munlockall2 system call allows a user to simply
> > clear the MCL_FUTURE flag.
>
> This is hard. Maybe we shouldn't have wired up anything other than
> x86. That's what we usually do with new syscalls.
Yeah I think so.
You haven't wired it up properly on powerpc, but I haven't mentioned it because
I'd rather we did it.
cheers
^ permalink raw reply
* Re: [PATCH v8 2/9] nvmem: Add a simple NVMEM framework for consumers
From: Srinivas Kandagatla @ 2015-07-22 7:26 UTC (permalink / raw)
To: Stefan Wahren, Greg Kroah-Hartman,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: wxt-TNX95d0MmH7DzftRWevZcw, linux-api-u79uwXL29TY76Z2rM5mHXA,
Rob Herring, sboyd-sgV2jX0FEOL9JmXXK+q4OQ, arnd-r2nGTMty4D4,
s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
mporter-OWPKS81ov/FWk0Htik3J/w,
linux-arm-msm-u79uwXL29TY76Z2rM5mHXA, Maxime Ripard,
pantelis.antoniou-OWPKS81ov/FWk0Htik3J/w, Mark Brown,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1590184041.70825.1437495942088.JavaMail.open-xchange-0SF9iQWekqLc2Cnrm9MUdsgmgJlYmuWJ@public.gmane.org>
Thanks Stefan,
On 21/07/15 17:25, Stefan Wahren wrote:
>> +
>> >+ addr = of_get_property(cell_np, "reg", &len);
>> >+ if (!addr || (len < 2 * sizeof(int))) {
> I'm not sure, but shouldn't be sizeof(u32) more portable?
>
yes it makes sense, I will change it.
>> >[...]
>> >+
>> >+ addr = of_get_property(cell_np, "bits", &len);
>> >+ if (addr && len == (2 * sizeof(int))) {
> dito
yep.
--srini
^ permalink raw reply
* Re: [RFC PATCH] getcpu_cache system call: caching current CPU number (x86)
From: Ondřej Bílka @ 2015-07-22 7:53 UTC (permalink / raw)
To: Mathieu Desnoyers
Cc: Linus Torvalds, Andy Lutomirski, Ben Maurer, Ingo Molnar,
libc-alpha, Andrew Morton, linux-api, rostedt, Paul E. McKenney,
Florian Weimer, Josh Triplett, Lai Jiangshan, Paul Turner,
Andrew Hunter, Peter Zijlstra
In-Reply-To: <2028561497.1088.1437502683664.JavaMail.zimbra-vg+e7yoeK/dWk0Htik3J/w@public.gmane.org>
On Tue, Jul 21, 2015 at 06:18:03PM +0000, Mathieu Desnoyers wrote:
> ----- On Jul 21, 2015, at 2:00 PM, Ondřej Bílka neleai@seznam.cz wrote:
>
> > On Tue, Jul 21, 2015 at 05:45:26PM +0000, Mathieu Desnoyers wrote:
> >> ----- On Jul 21, 2015, at 11:16 AM, Ondřej Bílka neleai@seznam.cz wrote:
> >>
> >> > On Tue, Jul 21, 2015 at 12:58:13PM +0000, Mathieu Desnoyers wrote:
> >> >> ----- On Jul 21, 2015, at 3:30 AM, Ondřej Bílka neleai@seznam.cz wrote:
> >> >>
> >> >> > On Tue, Jul 21, 2015 at 12:25:00AM +0000, Mathieu Desnoyers wrote:
> >> >> >> >> Does it solve the Wine problem? If Wine uses gs for something and
> >> >> >> >> calls a function that does this, Wine still goes boom, right?
> >> >> >> >
> >> >> >> > So the advantage of just making a global segment descriptor available
> >> >> >> > is that it's not *that* expensive to just save/restore segments. So
> >> >> >> > either wine could do it, or any library users would do it.
> >> >> >> >
> >> >> >> > But anyway, I'm not sure this is a good idea. The advantage of it is
> >> >> >> > that the kernel support really is _very_ minimal.
> >> >> >>
> >> >> >> Considering that we'd at least also want this feature on ARM and
> >> >> >> PowerPC 32/64, and that the gs segment selector approach clashes with
> >> >> >> existing apps (wine), I'm not sure that implementing a gs segment
> >> >> >> selector based approach to cpu number caching would lead to an overall
> >> >> >> decrease in complexity if it leads to performance similar to those of
> >> >> >> portable approaches.
> >> >> >>
> >> >> >> I'm perfectly fine with architecture-specific tweaks that lead to
> >> >> >> fast-path speedups, but if we have to bite the bullet and implement
> >> >> >> an approach based on TLS and registering a memory area at thread start
> >> >> >> through a system call on other architectures anyway, it might end up
> >> >> >> being less complex to add a new system call on x86 too, especially if
> >> >> >> fast path overhead is similar.
> >> >> >>
> >> >> >> But I'm inclined to think that some aspect of the question eludes me,
> >> >> >> especially given the amount of interest generated by the gs-segment
> >> >> >> selector approach. What am I missing ?
> >> >> >>
> >> >> > As I wrote before you don't have to bite bullet as I said before. It
> >> >> > suffices to create 128k element array with cpu for each tid, make that
> >> >> > mmapable file and userspace could get cpu with nearly same performance
> >> >> > without hacks.
> >> >>
> >> >> I don't see how this would be acceptable on memory-constrained embedded
> >> >> systems. They have multiple cores, and performance requirements, so
> >> >> having a fast getcpu would be useful there (e.g. telecom industry),
> >> >> but they clearly cannot afford a 512kB table per process just for that.
> >> >>
> >> > Which just means that you need more complicated api and implementation
> >> > for that but idea stays same. You would need syscalls
> >> > register/deregister_cpuid_idx that would give you index used instead
> >> > tid. A kernel would need to handle that many ids could be registered for
> >> > each thread and resize mmaped file in syscalls.
> >>
> >> I feel we're talking past each other here. What I propose is to implement
> >> a system call that registers a TLS area. It can be invoked at thread start.
> >> The kernel can then keep the current CPU number within that registered
> >> area up-to-date. This system call does not care how the TLS is implemented
> >> underneath.
> >>
> >> My understanding is that you are suggesting a way to speed up TLS accesses
> >> by creating a table indexed by TID. Although it might lead to interesting
> >> speed ups useful when reading the TLS, I don't see how you proposal is
> >> useful in addressing the problem of caching the current CPU number (other
> >> than possibly speeding up TLS accesses).
> >>
> >> Or am I missing something fundamental to your proposal ?
> >>
> > No, I still talk about getting cpu number. My first proposal is that
> > kernel allocates table of current cpu numbers accessed by tid. That
> > could process mmap and get cpu with cpu_tid_table[tid]. As you said that
> > size is problem I replied that you need to be more careful. Instead tid
> > you will use different id that you get with say register_cpucache, store
> > in tls variable and get cpu with cpu_cid_table[cid]. That decreases
> > space used to only threads that use this.
> >
> > A tls speedup was side remark when you would implement per-cpu page then
> > you could speedup tls. As tls access speed and getting tid these are
> > equivalent as you could easily implement one with other.
>
> Thanks for the clarification. There is then a fundamental question
> I need to ask: what is the upside of going for a dedicated array of
> current cpu number values rather than using a TLS variable ?
> The main downside I see with the array of cpu number is false sharing
> caused by having many current cpu number variables sitting on the same
> cache line. It seems like an overall performance loss there.
>
Its considerably simpler to implement as you don't need to mark tls
pages to avoid page fault in context switch, security issues where
attacker could try to unmap tls for possible privilege escalation if it
would write to different process etc.
And as for sharing it simply doesn't matter. Its mostly read only that
is written only on context switch so they will be resident in cache.
Also when you switch cpu then you get same cache miss from tls variable
so its same.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox