* Re: [RFC 1/3] pidfd: allow pidfd_open() on non-thread-group leaders
From: Florian Weimer @ 2023-12-08 13:15 UTC (permalink / raw)
To: Christian Brauner
Cc: Mathieu Desnoyers, Tycho Andersen, linux-kernel, linux-api,
Jan Kara, linux-fsdevel, Jens Axboe
In-Reply-To: <20231207-entdecken-selektiert-d5ce6dca6a80@brauner>
* Christian Brauner:
> File descriptors are reachable for all processes/threads that share a
> file descriptor table. Changing that means breaking core userspace
> assumptions about how file descriptors work. That's not going to happen
> as far as I'm concerned.
It already has happened, though? Threads are free to call
unshare(CLONE_FILES). I'm sure that we have applications out there that
expect this to work. At this point, the question is about whether we
want to acknowledge this possibility at the libc level or not.
Thanks,
Florian
^ permalink raw reply
* Re: [PATCH 3/4] listmount: small changes in semantics
From: Christian Brauner @ 2023-12-08 13:07 UTC (permalink / raw)
To: Miklos Szeredi
Cc: Serge E. Hallyn, Miklos Szeredi, Christian Brauner, linux-api,
linux-man, linux-security-module, Karel Zak, linux-fsdevel,
Ian Kent, David Howells, Al Viro
In-Reply-To: <CAJfpegs-uUEwKrEcmRE4WkzWet_A1f9mnM7UtFqM=szEUi+-1g@mail.gmail.com>
On Wed, Dec 06, 2023 at 09:24:45PM +0100, Miklos Szeredi wrote:
> On Wed, 6 Dec 2023 at 20:58, Serge E. Hallyn <serge@hallyn.com> wrote:
> >
> > On Tue, Nov 28, 2023 at 05:03:34PM +0100, Miklos Szeredi wrote:
>
> > > - if (!is_path_reachable(m, mnt->mnt_root, &rootmnt))
> > > - return capable(CAP_SYS_ADMIN) ? 0 : -EPERM;
> > > + if (!capable(CAP_SYS_ADMIN) &&
> >
> > Was there a reason to do the capable check first? In general,
> > checking capable() when not needed is frowned upon, as it will
> > set the PF_SUPERPRIV flag.
> >
>
> I synchronized the permission checking with statmount() without
> thinking about the order. I guess we can change the order back in
> both syscalls?
I can just change the order. It's mostly a question of what is more
expensive. If there's such unpleasant side-effects... then sure I'll
reorder.
> I also don't understand the reason behind the using the _noaudit()
> variant. Christian?
The reasoning is similar to the change in commit e7eda157c407 ("fs:
don't audit the capability check in simple_xattr_list()").
"The check being unconditional may lead to unwanted denials reported by
LSMs when a process has the capability granted by DAC, but denied by an
LSM. In the case of SELinux such denials are a problem, since they can't
be effectively filtered out via the policy and when not silenced, they
produce noise that may hide a true problem or an attack."
So for system calls like listmount() that we can expect to be called a
lot of times (findmnt etc at some point) this would needlessly spam
dmesg without any value. We can always change that to an explicit
capable() later.
^ permalink raw reply
* Re: [RFC 1/3] pidfd: allow pidfd_open() on non-thread-group leaders
From: Jens Axboe @ 2023-12-08 3:16 UTC (permalink / raw)
To: Christian Brauner, Florian Weimer
Cc: Mathieu Desnoyers, Tycho Andersen, linux-kernel, linux-api,
Jan Kara, linux-fsdevel
In-Reply-To: <20231207-entdecken-selektiert-d5ce6dca6a80@brauner>
On 12/7/23 3:58 PM, Christian Brauner wrote:
> [adjusting Cc as that's really a separate topic]
>
> On Thu, Nov 30, 2023 at 08:43:18PM +0100, Florian Weimer wrote:
>> * Mathieu Desnoyers:
>>
>>>>> I'd like to offer a userspace API which allows safe stashing of
>>>>> unreachable file descriptors on a service thread.
>
> Fwiw, systemd has a concept called the fdstore:
>
> https://systemd.io/FILE_DESCRIPTOR_STORE
>
> "The file descriptor store [...] allows services to upload during
> runtime additional fds to the service manager that it shall keep on its
> behalf. File descriptors are passed back to the service on subsequent
> activations, the same way as any socket activation fds are passed.
>
> [...]
>
> The primary use-case of this logic is to permit services to restart
> seamlessly (for example to update them to a newer version), without
> losing execution context, dropping pinned resources, terminating
> established connections or even just momentarily losing connectivity. In
> fact, as the file descriptors can be uploaded freely at any time during
> the service runtime, this can even be used to implement services that
> robustly handle abnormal termination and can recover from that without
> losing pinned resources."
>
>>
>>>> By "safe" here do you mean not accessible via pidfd_getfd()?
>>
>> No, unreachable by close/close_range/dup2/dup3. I expect we can do an
>> intra-process transfer using /proc, but I'm hoping for something nicer.
>
> File descriptors are reachable for all processes/threads that share a
> file descriptor table. Changing that means breaking core userspace
> assumptions about how file descriptors work. That's not going to happen
> as far as I'm concerned.
>
> We may consider additional security_* hooks in close*() and dup*(). That
> would allow you to utilize Landlock or BPF LSM to prevent file
> descriptors from being closed or duplicated. pidfd_getfd() is already
> blockable via security_file_receive().
>
> In general, messing with fds in that way is really not a good idea.
>
> If you need something that awkward, then you should go all the way and
> look at io_uring which basically has a separate fd-like handle called
> "fixed files".
>
> Fixed file indexes are separate file-descriptor like handles that can
> only be used from io_uring calls but not with the regular system call
> interface.
>
> IOW, you can refer to a file using an io_uring fixed index. The index to
> use can be chosen by userspace and can't be used with any regular
> fd-based system calls.
>
> The io_uring fd itself can be made a fixed file itself
>
> The only thing missing would be to turn an io_uring fixed file back into
> a regular file descriptor. That could probably be done by using
> receive_fd() and then installing that fd back into the caller's file
> descriptor table. But that would require an io_uring patch.
FWIW, since it was very trivial, I posted an rfc/test patch for just
that with a test case. It's here:
https://lore.kernel.org/io-uring/df0e24ff-f3a0-4818-8282-2a4e03b7b5a6@kernel.dk/
--
Jens Axboe
^ permalink raw reply
* Re: [RFC PATCH 01/11] mm/mempolicy: implement the sysfs-based weighted_interleave interface
From: Gregory Price @ 2023-12-08 0:11 UTC (permalink / raw)
To: Gregory Price, linux-mm, jgroves, ravis.opensrc, sthanneeru,
emirakhur, Hasan.Maruf, linux-doc, linux-fsdevel, linux-api,
linux-arch, linux-kernel, akpm, arnd, tglx, luto, mingo, bp,
dave.hansen, x86, hpa, mhocko, tj, ying.huang, corbet, rakie.kim,
hyeongtak.ji, honggyu.kim, vtavarespetr, peterz
In-Reply-To: <uxqkbmqbvcvx6wc3g2h6vhkutv5flrq6rslwdfs7pa6kknupwh@a245pbtfqfgj>
On Thu, Dec 07, 2023 at 01:56:07PM -0800, Davidlohr Bueso wrote:
> On Wed, 06 Dec 2023, Gregory Price wrote:
> > +
> > +What: /sys/kernel/mm/mempolicy/weighted_interleave/nodeN/
> > +Date: December 2023
> > +Contact: Linux memory management mailing list <linux-mm@kvack.org>
> > +Description: Configuration interface for accesses initiated from nodeN
> > +
> > + The directory to configure access initiator weights for nodeN.
> > +
> > + Possible numa nodes which have not been marked as a CPU node
> > + at boot will not have a nodeN directory made for them at boot.
>
> This could be better rephrased without the negation. ie:
>
> "Only numa nodes with CPUs (compute) will have a nodeN directory."
>
I thought documentation was supposed to be as confusing as possible.
lol I'll update it. reading it now, this is awful.
> > + Hotplug for CPU nodes is not supported.
>
> Can this even happen? Hot-adding a previously offlined CPU won't change/add a
> new numa node. So just rm the line altogether?
>
I... have no idea. In that sense, aye aye!
> > +static ssize_t node_weight_show(struct kobject *kobj,
> > + struct kobj_attribute *attr, char *buf)
> > +
> > +static ssize_t node_weight_store(struct kobject *kobj,
> > + struct kobj_attribute *attr,
> > + const char *buf, size_t count)
>
> iw_table will need some (basic) form of serialization.
>
originally the SKH group recommended a serialized "N*W,N*W,..." format,
but this doesn't work for a matrix.
Possibly i could add `N-M*W,...;N-M*W,...` and add a nodeN/weightlist
interface that lets you acquire the whole iw_table for one or more
nodes. Might be a nice extension.
I figured there is an aversion to multi-value sysfs files, so I reverted
back to a one-file-one-value file. If there is a preference for the
fully serialized methods, I'll happily add those. Easy enough and I
already have the code to parse it.
~Gregory
^ permalink raw reply
* Re: [RFC PATCH 07/11] mm/mempolicy: add userland mempolicy arg structure
From: Gregory Price @ 2023-12-08 0:05 UTC (permalink / raw)
To: Arnd Bergmann
Cc: Gregory Price, linux-mm, jgroves, ravis.opensrc, sthanneeru,
emirakhur, Hasan.Maruf, linux-doc, linux-fsdevel, linux-api,
Linux-Arch, linux-kernel, Andrew Morton, Thomas Gleixner,
Andy Lutomirski, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Michal Hocko, Tejun Heo, ying.huang,
Jonathan Corbet, rakie.kim, hyeongtak.ji, honggyu.kim,
vtavarespetr, Peter Zijlstra, Frank van der Linden
In-Reply-To: <cddbf290-021a-49d5-8729-e98cb099ff67@app.fastmail.com>
On Thu, Dec 07, 2023 at 04:43:03PM +0100, Arnd Bergmann wrote:
> On Thu, Dec 7, 2023, at 15:58, Gregory Price wrote:
> > On Thu, Dec 07, 2023 at 08:13:22AM +0100, Arnd Bergmann wrote:
> >> On Thu, Dec 7, 2023, at 01:27, Gregory Price wrote:
> >>
> >> Aside from this, you should avoid holes in the data structure.
> >> On 64-bit architectures, the layout above has holes after
> >> policy_node and after addr_node.
> >>
> >> Arnd
> >
> > doh, clearly i didn't stop to think about alignment. Good eye.
> > I'll redo this with __u/s members and fix the holes.
> >
> > Didn't stop to think about compat pointers. I don't think the
> > u64_to_user_ptr pattern is offensive, so i'll make that change.
> > At least I don't see what the other options are beyond compat.
>
> Ok, sounds good.
>
> I see you already call wrappers for compat mode to convert
> iovec and nodemask layouts for the indirect pointers, and they
> look correct. If you wanted to do handle the compat syscalls
> using the same entry point, you could add the same kind of
> helper to copy the mempolicy args from user space with an
> optional conversion, but not having to do this is clearly
> easier.
>
> Arnd
I don't know that either is easier, it's basically just what annoying
way do you want to handle this annoying problem. I'll poke at it
and decide which one I hate less.
One thing i didn't really think about, probably the iovec/len
fields should just be args for mbind2, rather than embedded in
mpol_args - since mpol_args is supposed to describe the mpol
while the iovec/len describes what it applies to.
Simplifies the mpol_args structure a bit. Doesn't change the handling
at all. (bit of a rubber ducky comment here)
As always, I appreciate the input
~Gregory
^ permalink raw reply
* Re: [RFC 1/3] pidfd: allow pidfd_open() on non-thread-group leaders
From: Christian Brauner @ 2023-12-07 22:58 UTC (permalink / raw)
To: Florian Weimer
Cc: Mathieu Desnoyers, Tycho Andersen, linux-kernel, linux-api,
Jan Kara, linux-fsdevel, Jens Axboe
In-Reply-To: <87ttp3rprd.fsf@oldenburg.str.redhat.com>
[adjusting Cc as that's really a separate topic]
On Thu, Nov 30, 2023 at 08:43:18PM +0100, Florian Weimer wrote:
> * Mathieu Desnoyers:
>
> >>> I'd like to offer a userspace API which allows safe stashing of
> >>> unreachable file descriptors on a service thread.
Fwiw, systemd has a concept called the fdstore:
https://systemd.io/FILE_DESCRIPTOR_STORE
"The file descriptor store [...] allows services to upload during
runtime additional fds to the service manager that it shall keep on its
behalf. File descriptors are passed back to the service on subsequent
activations, the same way as any socket activation fds are passed.
[...]
The primary use-case of this logic is to permit services to restart
seamlessly (for example to update them to a newer version), without
losing execution context, dropping pinned resources, terminating
established connections or even just momentarily losing connectivity. In
fact, as the file descriptors can be uploaded freely at any time during
the service runtime, this can even be used to implement services that
robustly handle abnormal termination and can recover from that without
losing pinned resources."
>
> >> By "safe" here do you mean not accessible via pidfd_getfd()?
>
> No, unreachable by close/close_range/dup2/dup3. I expect we can do an
> intra-process transfer using /proc, but I'm hoping for something nicer.
File descriptors are reachable for all processes/threads that share a
file descriptor table. Changing that means breaking core userspace
assumptions about how file descriptors work. That's not going to happen
as far as I'm concerned.
We may consider additional security_* hooks in close*() and dup*(). That
would allow you to utilize Landlock or BPF LSM to prevent file
descriptors from being closed or duplicated. pidfd_getfd() is already
blockable via security_file_receive().
In general, messing with fds in that way is really not a good idea.
If you need something that awkward, then you should go all the way and
look at io_uring which basically has a separate fd-like handle called
"fixed files".
Fixed file indexes are separate file-descriptor like handles that can
only be used from io_uring calls but not with the regular system call
interface.
IOW, you can refer to a file using an io_uring fixed index. The index to
use can be chosen by userspace and can't be used with any regular
fd-based system calls.
The io_uring fd itself can be made a fixed file itself
The only thing missing would be to turn an io_uring fixed file back into
a regular file descriptor. That could probably be done by using
receive_fd() and then installing that fd back into the caller's file
descriptor table. But that would require an io_uring patch.
^ permalink raw reply
* Re: [RFC PATCH 01/11] mm/mempolicy: implement the sysfs-based weighted_interleave interface
From: Davidlohr Bueso @ 2023-12-07 22:17 UTC (permalink / raw)
To: Gregory Price, linux-mm, jgroves, ravis.opensrc, sthanneeru,
emirakhur, Hasan.Maruf, linux-doc, linux-fsdevel, linux-api,
linux-arch, linux-kernel, akpm, arnd, tglx, luto, mingo, bp,
dave.hansen, x86, hpa, mhocko, tj, ying.huang, gregory.price,
corbet, rakie.kim, hyeongtak.ji, honggyu.kim, vtavarespetr,
peterz
In-Reply-To: <uxqkbmqbvcvx6wc3g2h6vhkutv5flrq6rslwdfs7pa6kknupwh@a245pbtfqfgj>
On Thu, 07 Dec 2023, Davidlohr Bueso wrote:
>fyi Rakie's tag needs to be last, per the From.
sorry no, quite the opposite, never mind this :)
^ permalink raw reply
* Re: [RFC PATCH 01/11] mm/mempolicy: implement the sysfs-based weighted_interleave interface
From: Davidlohr Bueso @ 2023-12-07 21:56 UTC (permalink / raw)
To: Gregory Price
Cc: linux-mm, jgroves, ravis.opensrc, sthanneeru, emirakhur,
Hasan.Maruf, linux-doc, linux-fsdevel, linux-api, linux-arch,
linux-kernel, akpm, arnd, tglx, luto, mingo, bp, dave.hansen, x86,
hpa, mhocko, tj, ying.huang, gregory.price, corbet, rakie.kim,
hyeongtak.ji, honggyu.kim, vtavarespetr, peterz
In-Reply-To: <20231207002759.51418-2-gregory.price@memverge.com>
On Wed, 06 Dec 2023, Gregory Price wrote:
>Signed-off-by: Rakie Kim <rakie.kim@sk.com>
>Signed-off-by: Honggyu Kim <honggyu.kim@sk.com>
>Co-developed-by: Gregory Price <gregory.price@memverge.com>
>Signed-off-by: Gregory Price <gregory.price@memverge.com>
>Co-developed-by: Hyeongtak Ji <hyeongtak.ji@sk.com>
>Signed-off-by: Hyeongtak Ji <hyeongtak.ji@sk.com>
fyi Rakie's tag needs to be last, per the From.
...
>+What: /sys/kernel/mm/mempolicy/weighted_interleave/
>+Date: December 2023
>+Contact: Linux memory management mailing list <linux-mm@kvack.org>
>+Description: Configuration Interface for the Weighted Interleave policy
>+
>+What: /sys/kernel/mm/mempolicy/weighted_interleave/nodeN/
>+Date: December 2023
>+Contact: Linux memory management mailing list <linux-mm@kvack.org>
>+Description: Configuration interface for accesses initiated from nodeN
>+
>+ The directory to configure access initiator weights for nodeN.
>+
>+ Possible numa nodes which have not been marked as a CPU node
>+ at boot will not have a nodeN directory made for them at boot.
This could be better rephrased without the negation. ie:
"Only numa nodes with CPUs (compute) will have a nodeN directory."
>+ Hotplug for CPU nodes is not supported.
Can this even happen? Hot-adding a previously offlined CPU won't change/add a
new numa node. So just rm the line altogether?
>+
>+What: /sys/kernel/mm/mempolicy/weighted_interleave/nodeN/nodeM
>+ /sys/kernel/mm/mempolicy/weighted_interleave/nodeN/nodeM/weight
>+Date: December 2023
>+Contact: Linux memory management mailing list <linux-mm@kvack.org>
>+Description: Configuration interface for target nodes accessed from nodeNN
>+
>+ The interleave weight for a memory node (M) from initiating
>+ node (N). These weights are utilized by processes which have set
>+ the mempolicy to MPOL_WEIGHTED_INTERLEAVE and have opted into
>+ global weights by omitting a task-local weight array.
>+
>+ These weights only affect new allocations, and changes at runtime
>+ will not cause migrations on already allocated pages.
>+
>+ If the weight of 0 is desired, the appropriate way to do this is
>+ by removing the node from the weighted interleave nodemask.
>+
>+ Minimum weight: 1
>+ Maximum weight: 255
>diff --git a/mm/mempolicy.c b/mm/mempolicy.c
>index 10a590ee1c89..ce332b5e7a03 100644
>--- a/mm/mempolicy.c
>+++ b/mm/mempolicy.c
>@@ -131,6 +131,11 @@ static struct mempolicy default_policy = {
>
> static struct mempolicy preferred_node_policy[MAX_NUMNODES];
>
>+struct interleave_weight_table {
>+ unsigned char weights[MAX_NUMNODES];
>+};
>+static struct interleave_weight_table *iw_table;
>+
> /**
> * numa_nearest_node - Find nearest node by state
> * @node: Node id to start the search
>@@ -3067,3 +3072,224 @@ void mpol_to_str(char *buffer, int maxlen, struct mempolicy *pol)
> p += scnprintf(p, buffer + maxlen - p, ":%*pbl",
> nodemask_pr_args(&nodes));
> }
>+
>+struct iw_node_info {
>+ struct kobject kobj;
>+ int src;
>+ int dst;
>+};
>+
>+static ssize_t node_weight_show(struct kobject *kobj,
>+ struct kobj_attribute *attr, char *buf)
>+{
>+ struct iw_node_info *node_info = container_of(kobj, struct iw_node_info,
>+ kobj);
>+ return sysfs_emit(buf, "%d\n",
>+ iw_table[node_info->src].weights[node_info->dst]);
>+}
>+
>+static ssize_t node_weight_store(struct kobject *kobj,
>+ struct kobj_attribute *attr,
>+ const char *buf, size_t count)
>+{
>+ unsigned char weight = 0;
>+ struct iw_node_info *node_info = NULL;
>+
>+ node_info = container_of(kobj, struct iw_node_info, kobj);
>+
>+ if (kstrtou8(buf, 0, &weight) || !weight)
>+ return -EINVAL;
>+
>+ iw_table[node_info->src].weights[node_info->dst] = weight;
>+
>+ return count;
>+}
iw_table will need some (basic) form of serialization.
...
>+static int __init mempolicy_sysfs_init(void)
>+{
>+ int err, nid;
>+ int cpunodes = 0;
>+ struct kobject *root_kobj;
>+
>+ for_each_node_state(nid, N_CPU)
>+ cpunodes += 1;
>+ iw_table = kmalloc_array(cpunodes, sizeof(*iw_table), GFP_KERNEL);
>+ if (!iw_table) {
>+ pr_err("failed to create interleave weight table\n");
>+ err = -ENOMEM;
>+ goto fail_obj;
No ref here yet, just return -ENOMEM.
>+ }
>+ memset(iw_table, 1, cpunodes * sizeof(*iw_table));
>+
>+ root_kobj = kzalloc(sizeof(struct kobject), GFP_KERNEL);
>+ if (!root_kobj)
>+ return -ENOMEM;
>+
>+ kobject_init(root_kobj, &mempolicy_kobj_ktype);
>+ err = kobject_add(root_kobj, mm_kobj, "mempolicy");
>+ if (err) {
>+ pr_err("failed to add kobject to the system\n");
>+ goto fail_obj;
>+ }
>+
>+ err = sysfs_create_group(root_kobj, &mempolicy_attr_group);
>+ if (err) {
>+ pr_err("failed to register mempolicy group\n");
>+ goto fail_obj;
>+ }
>+
>+ err = add_weighted_interleave_group(root_kobj);
>+fail_obj:
>+ if (err)
>+ kobject_put(root_kobj);
>+ return err;
>+
>+}
>+late_initcall(mempolicy_sysfs_init);
>--
>2.39.1
>
>
^ permalink raw reply
* Re: [RFC 1/3] pidfd: allow pidfd_open() on non-thread-group leaders
From: Christian Brauner @ 2023-12-07 21:25 UTC (permalink / raw)
To: Tycho Andersen, Oleg Nesterov
Cc: Eric W . Biederman, linux-kernel, linux-api, Tycho Andersen,
Jan Kara, linux-fsdevel, Joel Fernandes
In-Reply-To: <20231207-weither-autopilot-8daee206e6c5@brauner>
> If these concerns are correct
So, ok. I misremebered this. The scenario I had been thinking of is
basically the following.
We have a thread-group with thread-group leader 1234 and a thread with
4567 in that thread-group. Assume current thread-group leader is tsk1
and the non-thread-group leader is tsk2. tsk1 uses struct pid *tg_pid
and tsk2 uses struct pid *t_pid. The struct pids look like this after
creation of both thread-group leader tsk1 and thread tsk2:
TGID 1234 TID 4567
tg_pid[PIDTYPE_PID] = tsk1 t_pid[PIDTYPE_PID] = tsk2
tg_pid[PIDTYPE_TGID] = tsk1 t_pid[PIDTYPE_TGID] = NULL
IOW, tsk2's struct pid has never been used as a thread-group leader and
thus PIDTYPE_TGID is NULL. Now assume someone does create pidfds for
tsk1 and for tsk2:
tg_pidfd = pidfd_open(tsk1) t_pidfd = pidfd_open(tsk2)
-> tg_pidfd->private_data = tg_pid -> t_pidfd->private_data = t_pid
So we stash away struct pid *tg_pid for a pidfd_open() on tsk1 and we
stash away struct pid *t_pid for a pidfd_open() on tsk2.
If we wait on that task via P_PIDFD we get:
/* waiting through pidfd */
waitid(P_PIDFD, tg_pidfd) waitid(P_PIDFD, t_pidfd)
tg_pid[PIDTYPE_TGID] == tsk1 t_pid[PIDTYPE_TGID] == NULL
=> succeeds => fails
Because struct pid *tg_pid is used a thread-group leader struct pid we
can wait on that tsk1. But we can't via the non-thread-group leader
pidfd because the struct pid *t_pid has never been used as a
thread-group leader.
Now assume, t_pid exec's and the struct pids are transfered. IIRC, we
get:
tg_pid[PIDTYPE_PID] = tsk2 t_pid[PIDTYPE_PID] = tsk1
tg_pid[PIDTYPE_TGID] = tsk2 t_pid[PIDTYPE_TGID] = NULL
If we wait on that task via P_PIDFD we get:
/* waiting through pidfd */
waitid(P_PIDFD, tg_pidfd) waitid(P_PIDFD, t_pid)
tg_pid[PIDTYPE_TGID] == tsk2 t_pid[PIDTYPE_TGID] == NULL
=> succeeds => fails
Which is what we want. So effectively this should all work and I
misremembered the struct pid linkage. So afaict we don't even have a
problem here which is great.
^ permalink raw reply
* [PATCH v3] ELF: AT_PAGE_SHIFT_MASK -- supply userspace with available page shifts
From: Alexey Dobriyan @ 2023-12-07 18:44 UTC (permalink / raw)
To: Andrew Morton
Cc: Florian Weimer, linux-kernel, linux-arch, linux-api, x86,
Eric Biederman, Kees Cook, linux-mm
In-Reply-To: <1d679805-8a82-44a4-ba14-49d4f28ff597@p183>
Report available page shifts in arch independent manner, so that
userspace developers won't have to parse /proc/cpuinfo hunting
for arch specific strings.
Main users are supposed to be libhugetlbfs-like libraries which try
to abstract huge mappings across multiple architectures. Regular code
which queries hugepage support before using them benefits too because
it doesn't have to deal with descriptors and parsing sysfs hierarchies
while enjoying the simplicity and speed of getauxval(AT_PAGE_SHIFT_MASK).
Note!
This is strictly for userspace, if some page size is shutdown due
to kernel command line option or CPU bug workaround, than it must
not be reported in aux vector!
x86_64 machine with 1 GiB pages:
00000030 06 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00
00000040 1d 00 00 00 00 00 00 00 00 10 20 40 00 00 00 00
x86_64 machine with 2 MiB pages only:
00000030 06 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00
00000040 1d 00 00 00 00 00 00 00 00 10 20 00 00 00 00 00
AT_PAGESZ always reports one smallest page size which is not interesting.
Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
---
v3: better comment and changelog
v2: switch to page shifts, rename to ARCH_AT_PAGE_SHIFT_MASK
arch/x86/include/asm/elf.h | 12 ++++++++++++
fs/binfmt_elf.c | 3 +++
include/uapi/linux/auxvec.h | 13 +++++++++++++
3 files changed, 28 insertions(+)
--- a/arch/x86/include/asm/elf.h
+++ b/arch/x86/include/asm/elf.h
@@ -358,6 +358,18 @@ else if (IS_ENABLED(CONFIG_IA32_EMULATION)) \
#define COMPAT_ELF_ET_DYN_BASE (TASK_UNMAPPED_BASE + 0x1000000)
+#define ARCH_AT_PAGE_SHIFT_MASK \
+ do { \
+ u32 val = 1 << 12; \
+ if (boot_cpu_has(X86_FEATURE_PSE)) { \
+ val |= 1 << 21; \
+ } \
+ if (boot_cpu_has(X86_FEATURE_GBPAGES)) { \
+ val |= 1 << 30; \
+ } \
+ NEW_AUX_ENT(AT_PAGE_SHIFT_MASK, val); \
+ } while (0)
+
#endif /* !CONFIG_X86_32 */
#define VDSO_CURRENT_BASE ((unsigned long)current->mm->context.vdso)
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -240,6 +240,9 @@ create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec,
#endif
NEW_AUX_ENT(AT_HWCAP, ELF_HWCAP);
NEW_AUX_ENT(AT_PAGESZ, ELF_EXEC_PAGESIZE);
+#ifdef ARCH_AT_PAGE_SHIFT_MASK
+ ARCH_AT_PAGE_SHIFT_MASK;
+#endif
NEW_AUX_ENT(AT_CLKTCK, CLOCKS_PER_SEC);
NEW_AUX_ENT(AT_PHDR, phdr_addr);
NEW_AUX_ENT(AT_PHENT, sizeof(struct elf_phdr));
--- a/include/uapi/linux/auxvec.h
+++ b/include/uapi/linux/auxvec.h
@@ -33,6 +33,19 @@
#define AT_RSEQ_FEATURE_SIZE 27 /* rseq supported feature size */
#define AT_RSEQ_ALIGN 28 /* rseq allocation alignment */
+/*
+ * All page sizes supported by CPU encoded as bitmask.
+ *
+ * Example: x86_64 system with pse, pdpe1gb /proc/cpuinfo flags
+ * reports 4 KiB, 2 MiB and 1 GiB page support.
+ *
+ * $ LD_SHOW_AUXV=1 $(which true) | grep -e AT_PAGE_SHIFT_MASK
+ * AT_PAGE_SHIFT_MASK: 0x40201000
+ *
+ * For 2^64 hugepage support please contact your Universe sales representative.
+ */
+#define AT_PAGE_SHIFT_MASK 29
+
#define AT_EXECFN 31 /* filename of program */
#ifndef AT_MINSIGSTKSZ
^ permalink raw reply
* Re: [RFC 1/3] pidfd: allow pidfd_open() on non-thread-group leaders
From: Christian Brauner @ 2023-12-07 17:57 UTC (permalink / raw)
To: Tycho Andersen, Oleg Nesterov
Cc: Eric W . Biederman, linux-kernel, linux-api, Tycho Andersen,
Jan Kara, linux-fsdevel, Joel Fernandes
In-Reply-To: <ZWoKbHJ0152tiGeD@tycho.pizza>
On Fri, Dec 01, 2023 at 09:31:40AM -0700, Tycho Andersen wrote:
> On Thu, Nov 30, 2023 at 10:57:01AM -0700, Tycho Andersen wrote:
> > On Thu, Nov 30, 2023 at 06:39:39PM +0100, Oleg Nesterov wrote:
> > > I think that wake_up_all(wait_pidfd) should have a single caller,
> > > do_notify_pidfd(). This probably means it should be shiftef from
> > > do_notify_parent() to exit_notify(), I am not sure...
>
> Indeed, below passes the tests without issue and is much less ugly.
So I think I raised that question on another medium already but what
does the interaction with de_thread() look like?
Say some process creates pidfd for a thread in a non-empty thread-group
is created via CLONE_PIDFD. The pidfd_file->private_data is set to
struct pid of that task. The task the pidfd refers to later exec's.
Once it passed de_thread() the task the pidfd refers to assumes the
struct pid of the old thread-group leader and continues.
At the same time, the old thread-group leader now assumes the struct pid
of the task that just exec'd.
So after de_thread() the pidfd now referes to the old thread-group
leaders struct pid. Any subsequent operation will fail because the
process has already exited.
Basically, the pidfd now refers to the old thread-group leader and any
subsequent operation will fail even though the task still exists.
Conversely, if someone had created a pidfd that referred to the old
thread-group leader task then this pidfd will now suddenly refer to the
new thread-group leader task for the same reason: the struct pid's were
exchanged.
So this also means, iiuc, that the pidfd could now be passed to
waitid(P_PIFD) to retrieve the status of the old thread-group leader
that just got zapped.
And for the case where the pidfd referred to the old thread-group leader
task you would now suddenly _not_ be able to wait on that task anymore.
If these concerns are correct, then I think we need to decide what
semantics we want and how to handle this because that's not ok.
^ permalink raw reply
* Re: [RFC 1/3] pidfd: allow pidfd_open() on non-thread-group leaders
From: Tycho Andersen @ 2023-12-07 17:52 UTC (permalink / raw)
To: Christian Brauner
Cc: Oleg Nesterov, Eric W . Biederman, linux-kernel, linux-api,
Tycho Andersen, Jan Kara, linux-fsdevel, Joel Fernandes
In-Reply-To: <20231207-netzhaut-wachen-81c34f8ee154@brauner>
On Thu, Dec 07, 2023 at 06:21:18PM +0100, Christian Brauner wrote:
> [Cc fsdevel & Jan because we had some discussions about fanotify
> returning non-thread-group pidfds. That's just for awareness or in case
> this might need special handling.]
>
> On Thu, Nov 30, 2023 at 09:39:44AM -0700, Tycho Andersen wrote:
> > From: Tycho Andersen <tandersen@netflix.com>
> >
> > We are using the pidfd family of syscalls with the seccomp userspace
> > notifier. When some thread triggers a seccomp notification, we want to do
> > some things to its context (munge fd tables via pidfd_getfd(), maybe write
> > to its memory, etc.). However, threads created with ~CLONE_FILES or
> > ~CLONE_VM mean that we can't use the pidfd family of syscalls for this
> > purpose, since their fd table or mm are distinct from the thread group
> > leader's. In this patch, we relax this restriction for pidfd_open().
> >
> > In order to avoid dangling poll() users we need to notify pidfd waiters
> > when individual threads die, but once we do that all the other machinery
> > seems to work ok viz. the tests. But I suppose there are more cases than
> > just this one.
> >
> > Another weirdness is the open-coding of this vs. exporting using
> > do_notify_pidfd(). This particular location is after __exit_signal() is
> > called, which does __unhash_process() which kills ->thread_pid, so we need
> > to use the copy we have locally, vs do_notify_pid() which accesses it via
> > task_pid(). Maybe this suggests that the notification should live somewhere
> > in __exit_signals()? I just put it here because I saw we were already
> > testing if this task was the leader.
> >
> > Signed-off-by: Tycho Andersen <tandersen@netflix.com>
> > ---
>
> So we've always said that if there's a use-case for this then we're
> willing to support it. And I think that stance hasn't changed. I know
> that others have expressed interest in this as well.
>
> So currently the series only enables pidfds for threads to be created
> and allows notifications for threads. But all places that currently make
> use of pidfds refuse non-thread-group leaders. We can certainly proceed
> with a patch series that only enables creation and exit notification but
> we should also consider unlocking additional functionality:
>
> * audit of all callers that use pidfd_get_task()
>
> (1) process_madvise()
> (2) process_mrlease()
>
> I expect that both can handle threads just fine but we'd need an Ack
> from mm people.
>
> * pidfd_prepare() is used to create pidfds for:
>
> (1) CLONE_PIDFD via clone() and clone3()
> (2) SCM_PIDFD and SO_PEERPIDFD
> (3) fanotify
>
> (1) is what this series here is about.
>
> For (2) we need to check whether fanotify would be ok to handle pidfds
> for threads. It might be fine but Jan will probably know more.
>
> For (3) the change doesn't matter because SCM_CREDS always use the
> thread-group leader. So even if we allowed the creation of pidfds for
> threads it wouldn't matter.
> * audit all callers of pidfd_pid() whether they could simply be switched
> to handle individual threads:
>
> (1) setns() handles threads just fine so this is safe to allow.
> (2) pidfd_getfd() I would like to keep restricted and essentially
> freeze new features for it.
>
> I'm not happy that we did didn't just implement it as an ioctl to
> the seccomp notifier. And I wouldn't oppose a patch that would add
> that functionality to the seccomp notifier itself. But that's a
> separate topic.
> (3) pidfd_send_signal(). I think that one is the most interesting on
> to allow signaling individual threads. I'm not sure that you need
> to do this right now in this patch but we need to think about what
> we want to do there.
This all sounds reasonable to me, I can take a look as time permits.
pidfd_send_signal() at the very least would have been useful while
writing these tests.
Tycho
^ permalink raw reply
* Re: [RFC 1/3] pidfd: allow pidfd_open() on non-thread-group leaders
From: Christian Brauner @ 2023-12-07 17:21 UTC (permalink / raw)
To: Tycho Andersen
Cc: Oleg Nesterov, Eric W . Biederman, linux-kernel, linux-api,
Tycho Andersen, Jan Kara, linux-fsdevel, Joel Fernandes
In-Reply-To: <20231130163946.277502-1-tycho@tycho.pizza>
[Cc fsdevel & Jan because we had some discussions about fanotify
returning non-thread-group pidfds. That's just for awareness or in case
this might need special handling.]
On Thu, Nov 30, 2023 at 09:39:44AM -0700, Tycho Andersen wrote:
> From: Tycho Andersen <tandersen@netflix.com>
>
> We are using the pidfd family of syscalls with the seccomp userspace
> notifier. When some thread triggers a seccomp notification, we want to do
> some things to its context (munge fd tables via pidfd_getfd(), maybe write
> to its memory, etc.). However, threads created with ~CLONE_FILES or
> ~CLONE_VM mean that we can't use the pidfd family of syscalls for this
> purpose, since their fd table or mm are distinct from the thread group
> leader's. In this patch, we relax this restriction for pidfd_open().
>
> In order to avoid dangling poll() users we need to notify pidfd waiters
> when individual threads die, but once we do that all the other machinery
> seems to work ok viz. the tests. But I suppose there are more cases than
> just this one.
>
> Another weirdness is the open-coding of this vs. exporting using
> do_notify_pidfd(). This particular location is after __exit_signal() is
> called, which does __unhash_process() which kills ->thread_pid, so we need
> to use the copy we have locally, vs do_notify_pid() which accesses it via
> task_pid(). Maybe this suggests that the notification should live somewhere
> in __exit_signals()? I just put it here because I saw we were already
> testing if this task was the leader.
>
> Signed-off-by: Tycho Andersen <tandersen@netflix.com>
> ---
So we've always said that if there's a use-case for this then we're
willing to support it. And I think that stance hasn't changed. I know
that others have expressed interest in this as well.
So currently the series only enables pidfds for threads to be created
and allows notifications for threads. But all places that currently make
use of pidfds refuse non-thread-group leaders. We can certainly proceed
with a patch series that only enables creation and exit notification but
we should also consider unlocking additional functionality:
* audit of all callers that use pidfd_get_task()
(1) process_madvise()
(2) process_mrlease()
I expect that both can handle threads just fine but we'd need an Ack
from mm people.
* pidfd_prepare() is used to create pidfds for:
(1) CLONE_PIDFD via clone() and clone3()
(2) SCM_PIDFD and SO_PEERPIDFD
(3) fanotify
(1) is what this series here is about.
For (2) we need to check whether fanotify would be ok to handle pidfds
for threads. It might be fine but Jan will probably know more.
For (3) the change doesn't matter because SCM_CREDS always use the
thread-group leader. So even if we allowed the creation of pidfds for
threads it wouldn't matter.
* audit all callers of pidfd_pid() whether they could simply be switched
to handle individual threads:
(1) setns() handles threads just fine so this is safe to allow.
(2) pidfd_getfd() I would like to keep restricted and essentially
freeze new features for it.
I'm not happy that we did didn't just implement it as an ioctl to
the seccomp notifier. And I wouldn't oppose a patch that would add
that functionality to the seccomp notifier itself. But that's a
separate topic.
(3) pidfd_send_signal(). I think that one is the most interesting on
to allow signaling individual threads. I'm not sure that you need
to do this right now in this patch but we need to think about what
we want to do there.
^ permalink raw reply
* [PATCH v2 3/3] clone: allow CLONE_THREAD | CLONE_PIDFD together
From: Tycho Andersen @ 2023-12-07 17:09 UTC (permalink / raw)
To: Christian Brauner
Cc: Oleg Nesterov, Eric W . Biederman, linux-kernel, linux-api,
Tycho Andersen, Tycho Andersen
In-Reply-To: <20231207170946.130823-1-tycho@tycho.pizza>
From: Tycho Andersen <tandersen@netflix.com>
This removes the restriction of CLONE_THREAD | CLONE_PIDFD being specified
together. Assuming the previous patch sorts out all the thorny issues this
should be safe. I've left it as a separate patch since it is not strictly
necessary as a usecase for us, but might be nice? Perhaps we want to wait
until someone actually needs it though.
Signed-off-by: Tycho Andersen <tandersen@netflix.com>
---
kernel/fork.c | 3 +--
.../selftests/pidfd/pidfd_non_tgl_test.c | 17 +++++++++++++++++
2 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/kernel/fork.c b/kernel/fork.c
index eef15c93f6cf..ada476f38b56 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -2302,9 +2302,8 @@ __latent_entropy struct task_struct *copy_process(
/*
* - CLONE_DETACHED is blocked so that we can potentially
* reuse it later for CLONE_PIDFD.
- * - CLONE_THREAD is blocked until someone really needs it.
*/
- if (clone_flags & (CLONE_DETACHED | CLONE_THREAD))
+ if (clone_flags & CLONE_DETACHED)
return ERR_PTR(-EINVAL);
}
diff --git a/tools/testing/selftests/pidfd/pidfd_non_tgl_test.c b/tools/testing/selftests/pidfd/pidfd_non_tgl_test.c
index e3992f2d88cf..dfd6a2cd85a3 100644
--- a/tools/testing/selftests/pidfd/pidfd_non_tgl_test.c
+++ b/tools/testing/selftests/pidfd/pidfd_non_tgl_test.c
@@ -305,6 +305,22 @@ static int test_non_tgl_exit(void)
return ret;
}
+static int test_clone_thread_pidfd(void)
+{
+ pid_t pid;
+ int flags = CLONE_THREAD | CLONE_VM | CLONE_SIGHAND | CLONE_PIDFD;
+ int pidfd;
+
+ pid = clone(thread_sleep, stack + STACK_SIZE, flags, NULL, &pidfd);
+ if (pid < 0) {
+ perror("clone");
+ return KSFT_FAIL;
+ }
+
+ close(pidfd);
+ return KSFT_PASS;
+}
+
#define T(x) { x, #x }
struct pidfd_non_tgl_test {
int (*fn)();
@@ -313,6 +329,7 @@ struct pidfd_non_tgl_test {
T(test_non_tgl_basic),
T(test_non_tgl_exec),
T(test_non_tgl_exit),
+ T(test_clone_thread_pidfd),
};
#undef T
--
2.34.1
^ permalink raw reply related
* [PATCH v2 2/3] selftests/pidfd: add non-thread-group leader tests
From: Tycho Andersen @ 2023-12-07 17:09 UTC (permalink / raw)
To: Christian Brauner
Cc: Oleg Nesterov, Eric W . Biederman, linux-kernel, linux-api,
Tycho Andersen, Tycho Andersen
In-Reply-To: <20231207170946.130823-1-tycho@tycho.pizza>
From: Tycho Andersen <tandersen@netflix.com>
This adds a family of tests for various behaviors for pidfds of
non-thread-group leaders.
Signed-off-by: Tycho Andersen <tandersen@netflix.com>
---
tools/testing/selftests/pidfd/.gitignore | 1 +
tools/testing/selftests/pidfd/Makefile | 3 +-
.../selftests/pidfd/pidfd_non_tgl_test.c | 339 ++++++++++++++++++
3 files changed, 342 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/pidfd/.gitignore b/tools/testing/selftests/pidfd/.gitignore
index 973198a3ec3d..e7532e84a34a 100644
--- a/tools/testing/selftests/pidfd/.gitignore
+++ b/tools/testing/selftests/pidfd/.gitignore
@@ -6,3 +6,4 @@ pidfd_wait
pidfd_fdinfo_test
pidfd_getfd_test
pidfd_setns_test
+pidfd_non_tgl_test
diff --git a/tools/testing/selftests/pidfd/Makefile b/tools/testing/selftests/pidfd/Makefile
index d731e3e76d5b..50e3aa9de05a 100644
--- a/tools/testing/selftests/pidfd/Makefile
+++ b/tools/testing/selftests/pidfd/Makefile
@@ -2,7 +2,8 @@
CFLAGS += -g $(KHDR_INCLUDES) -pthread -Wall
TEST_GEN_PROGS := pidfd_test pidfd_fdinfo_test pidfd_open_test \
- pidfd_poll_test pidfd_wait pidfd_getfd_test pidfd_setns_test
+ pidfd_poll_test pidfd_wait pidfd_getfd_test pidfd_setns_test \
+ pidfd_non_tgl_test
include ../lib.mk
diff --git a/tools/testing/selftests/pidfd/pidfd_non_tgl_test.c b/tools/testing/selftests/pidfd/pidfd_non_tgl_test.c
new file mode 100644
index 000000000000..e3992f2d88cf
--- /dev/null
+++ b/tools/testing/selftests/pidfd/pidfd_non_tgl_test.c
@@ -0,0 +1,339 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <sys/socket.h>
+#include <limits.h>
+#include <string.h>
+#include <signal.h>
+#include <syscall.h>
+#include <sched.h>
+#include <poll.h>
+
+#include "../kselftest.h"
+#include "pidfd.h"
+
+// glibc defaults to 8MB stacks
+#define STACK_SIZE (8 * 1024 * 1024)
+static char stack[STACK_SIZE];
+
+static int thread_sleep(void *)
+{
+ while (1)
+ sleep(100);
+ return 1;
+}
+
+static int fork_task_with_thread(int (*fn)(void *), int sk_pair[2],
+ pid_t *tgl, pid_t *thread, int *tgl_pidfd,
+ int *thread_pidfd)
+{
+ *tgl_pidfd = *thread_pidfd = -1;
+
+ *tgl = fork();
+ if (*tgl < 0) {
+ perror("fork");
+ return -1;
+ }
+
+ if (!*tgl) {
+ int flags = CLONE_THREAD | CLONE_VM | CLONE_SIGHAND;
+ pid_t t;
+
+ t = clone(fn, stack + STACK_SIZE, flags, sk_pair);
+ if (t < 0) {
+ perror("clone");
+ exit(1);
+ }
+
+ close(sk_pair[1]);
+
+ if (write(sk_pair[0], &t, sizeof(t)) != sizeof(t)) {
+ perror("read");
+ exit(1);
+ }
+
+ // wait to get killed for various reasons by the tests.
+ while (1)
+ sleep(100);
+ }
+
+ errno = 0;
+ if (read(sk_pair[1], thread, sizeof(*thread)) != sizeof(*thread)) {
+ perror("read");
+ goto cleanup;
+ }
+
+ *tgl_pidfd = sys_pidfd_open(*tgl, 0);
+ if (*tgl_pidfd < 0) {
+ perror("pidfd_open tgl");
+ goto cleanup;
+ }
+
+ *thread_pidfd = sys_pidfd_open(*thread, 0);
+ if (*thread_pidfd < 0) {
+ perror("pidfd");
+ goto cleanup;
+ }
+
+ return 0;
+
+cleanup:
+ kill(*tgl, SIGKILL);
+ if (*tgl_pidfd >= 0)
+ close(*tgl_pidfd);
+ if (*thread_pidfd >= 0)
+ close(*thread_pidfd);
+ return -1;
+}
+
+static int test_non_tgl_basic(void)
+{
+ pid_t tgl, thread;
+ int sk_pair[2], status;
+ int tgl_pidfd = -1, thread_pidfd = -1;
+ int ret = KSFT_FAIL;
+
+ if (socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sk_pair) < 0) {
+ ksft_print_msg("socketpair failed %s\n", strerror(errno));
+ return KSFT_FAIL;
+ }
+
+ if (fork_task_with_thread(thread_sleep, sk_pair, &tgl, &thread,
+ &tgl_pidfd, &thread_pidfd) < 0) {
+ return KSFT_FAIL;
+ }
+
+ /*
+ * KILL of a thread should still kill everyone
+ */
+ if (sys_pidfd_send_signal(thread_pidfd, SIGKILL, NULL, 0)) {
+ perror("pidfd_send_signal");
+ goto cleanup;
+ }
+
+ errno = 0;
+ if (waitpid(tgl, &status, 0) != tgl) {
+ perror("waitpid tgl");
+ goto cleanup;
+ }
+
+ if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGKILL) {
+ ksft_print_msg("bad exit status %x\n", status);
+ goto cleanup;
+ }
+
+ ret = KSFT_PASS;
+
+cleanup:
+ close(sk_pair[0]);
+ close(sk_pair[1]);
+ close(tgl_pidfd);
+ close(thread_pidfd);
+ return ret;
+}
+
+static int thread_exec(void *arg)
+{
+ int *sk_pair = arg;
+ pid_t thread;
+
+ if (read(sk_pair[0], &thread, sizeof(thread)) != sizeof(thread)) {
+ perror("read");
+ exit(1);
+ }
+
+ execlp("/bin/true", "/bin/true", NULL);
+ return 1;
+}
+
+static int test_non_tgl_exec(void)
+{
+ pid_t tgl, thread;
+ int sk_pair[2];
+ int tgl_pidfd = -1, thread_pidfd = -1;
+ int ret = KSFT_FAIL, ready;
+ struct pollfd pollfd;
+
+ if (socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sk_pair) < 0) {
+ ksft_print_msg("socketpair failed %s\n", strerror(errno));
+ return KSFT_FAIL;
+ }
+
+ if (fork_task_with_thread(thread_exec, sk_pair, &tgl, &thread,
+ &tgl_pidfd, &thread_pidfd) < 0) {
+ return KSFT_FAIL;
+ }
+
+ if (write(sk_pair[1], &thread, sizeof(thread)) != sizeof(thread)) {
+ perror("read");
+ goto cleanup;
+ }
+
+ // thread will exec(), so this pidfd should eventually be dead (i.e.
+ // poll should return).
+ pollfd.fd = thread_pidfd;
+ pollfd.events = POLLIN;
+
+ ready = poll(&pollfd, 1, -1);
+ if (ready == -1) {
+ perror("poll");
+ goto cleanup;
+ }
+
+ if (ready != 1) {
+ ksft_print_msg("bad poll result %d\n", ready);
+ goto cleanup;
+ }
+
+ if (pollfd.revents != POLLIN) {
+ ksft_print_msg("bad poll revents: %x\n", pollfd.revents);
+ goto cleanup;
+ }
+
+ errno = 0;
+ if (sys_pidfd_getfd(thread_pidfd, 0, 0) > 0) {
+ ksft_print_msg("got a real fd");
+ goto cleanup;
+ }
+
+ if (errno != ESRCH) {
+ ksft_print_msg("polling invalid thread didn't give ESRCH");
+ goto cleanup;
+ }
+
+ ret = KSFT_PASS;
+
+cleanup:
+ close(sk_pair[0]);
+ close(sk_pair[1]);
+ close(tgl_pidfd);
+ close(thread_pidfd);
+ return ret;
+}
+
+int thread_wait_exit(void *arg)
+{
+ int *sk_pair = arg;
+ pid_t thread;
+
+ if (read(sk_pair[0], &thread, sizeof(thread)) != sizeof(thread)) {
+ perror("read");
+ exit(1);
+ }
+
+ return 0;
+}
+
+static int test_non_tgl_exit(void)
+{
+ pid_t tgl, thread;
+ int sk_pair[2], status;
+ int tgl_pidfd = -1, thread_pidfd = -1;
+ int ret = KSFT_FAIL, ready;
+ struct pollfd pollfd;
+
+ if (socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sk_pair) < 0) {
+ ksft_print_msg("socketpair failed %s\n", strerror(errno));
+ return KSFT_FAIL;
+ }
+
+ if (fork_task_with_thread(thread_wait_exit, sk_pair, &tgl, &thread,
+ &tgl_pidfd, &thread_pidfd) < 0) {
+ return KSFT_FAIL;
+ }
+
+ if (write(sk_pair[1], &thread, sizeof(thread)) != sizeof(thread)) {
+ perror("write");
+ goto cleanup;
+ }
+
+ // thread will exit, so this pidfd should eventually be dead (i.e.
+ // poll should return).
+ pollfd.fd = thread_pidfd;
+ pollfd.events = POLLIN;
+
+ ready = poll(&pollfd, 1, -1);
+ if (ready == -1) {
+ perror("poll");
+ goto cleanup;
+ }
+
+ if (ready != 1) {
+ ksft_print_msg("bad poll result %d\n", ready);
+ goto cleanup;
+ }
+
+ if (pollfd.revents != POLLIN) {
+ ksft_print_msg("bad poll revents: %x\n", pollfd.revents);
+ goto cleanup;
+ }
+
+ errno = 0;
+ if (sys_pidfd_getfd(thread_pidfd, 0, 0) > 0) {
+ ksft_print_msg("got a real pidfd");
+ goto cleanup;
+ }
+
+ if (errno != ESRCH) {
+ ksft_print_msg("polling invalid thread didn't give ESRCH");
+ goto cleanup;
+ }
+
+ close(thread_pidfd);
+
+ // ok, but the thread group *leader* should still be alive
+ pollfd.fd = tgl_pidfd;
+ ready = poll(&pollfd, 1, 1);
+ if (ready == -1) {
+ perror("poll");
+ goto cleanup;
+ }
+
+ if (ready != 0) {
+ ksft_print_msg("polling leader returned something?! %x", pollfd.revents);
+ goto cleanup;
+ }
+
+ ret = KSFT_PASS;
+
+cleanup:
+ kill(tgl, SIGKILL);
+ if (waitpid(tgl, &status, 0) != tgl) {
+ perror("waitpid");
+ return KSFT_FAIL;
+ }
+
+ return ret;
+}
+
+#define T(x) { x, #x }
+struct pidfd_non_tgl_test {
+ int (*fn)();
+ const char *name;
+} tests[] = {
+ T(test_non_tgl_basic),
+ T(test_non_tgl_exec),
+ T(test_non_tgl_exit),
+};
+#undef T
+
+int main(int argc, char *argv[])
+{
+ int i, ret = EXIT_SUCCESS;
+
+ for (i = 0; i < ARRAY_SIZE(tests); i++) {
+ switch (tests[i].fn()) {
+ case KSFT_PASS:
+ ksft_test_result_pass("%s\n", tests[i].name);
+ break;
+ case KSFT_SKIP:
+ ksft_test_result_skip("%s\n", tests[i].name);
+ break;
+ default:
+ ret = EXIT_FAILURE;
+ ksft_test_result_fail("%s\n", tests[i].name);
+ break;
+ }
+ }
+
+ return ret;
+}
--
2.34.1
^ permalink raw reply related
* [PATCH v2 1/3] pidfd: allow pidfd_open() on non-thread-group leaders
From: Tycho Andersen @ 2023-12-07 17:09 UTC (permalink / raw)
To: Christian Brauner
Cc: Oleg Nesterov, Eric W . Biederman, linux-kernel, linux-api,
Tycho Andersen, Tycho Andersen
From: Tycho Andersen <tandersen@netflix.com>
We are using the pidfd family of syscalls with the seccomp userspace
notifier. When some thread triggers a seccomp notification, we want to do
some things to its context (munge fd tables via pidfd_getfd(), maybe write
to its memory, etc.). However, threads created with ~CLONE_FILES or
~CLONE_VM mean that we can't use the pidfd family of syscalls for this
purpose, since their fd table or mm are distinct from the thread group
leader's. In this patch, we relax this restriction for pidfd_open().
In order to avoid dangling poll() users we need to notify pidfd waiters
when individual threads die, but once we do that all the other machinery
seems to work ok viz. the tests. But I suppose there are more cases than
just this one.
Signed-off-by: Tycho Andersen <tandersen@netflix.com>
--
v2: unify pidfd notification to all go through do_notify_pidfd() inside of
__exit_signals() suggested by Oleg.
Link to v1: https://lore.kernel.org/all/20231130163946.277502-1-tycho@tycho.pizza/
---
include/linux/sched/signal.h | 1 +
kernel/exit.c | 3 +++
kernel/fork.c | 4 +---
kernel/pid.c | 11 +----------
kernel/signal.c | 5 +----
5 files changed, 7 insertions(+), 17 deletions(-)
diff --git a/include/linux/sched/signal.h b/include/linux/sched/signal.h
index 3499c1a8b929..37d6b4e4ab70 100644
--- a/include/linux/sched/signal.h
+++ b/include/linux/sched/signal.h
@@ -332,6 +332,7 @@ extern int kill_pid_usb_asyncio(int sig, int errno, sigval_t addr, struct pid *,
extern int kill_pgrp(struct pid *pid, int sig, int priv);
extern int kill_pid(struct pid *pid, int sig, int priv);
extern __must_check bool do_notify_parent(struct task_struct *, int);
+extern void do_notify_pidfd(struct task_struct *task);
extern void __wake_up_parent(struct task_struct *p, struct task_struct *parent);
extern void force_sig(int);
extern void force_fatal_sig(int);
diff --git a/kernel/exit.c b/kernel/exit.c
index ee9f43bed49a..7bb6488ebd79 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -149,6 +149,9 @@ static void __exit_signal(struct task_struct *tsk)
struct tty_struct *tty;
u64 utime, stime;
+ /* Wake up all pidfd waiters */
+ do_notify_pidfd(tsk);
+
sighand = rcu_dereference_check(tsk->sighand,
lockdep_tasklist_lock_is_held());
spin_lock(&sighand->siglock);
diff --git a/kernel/fork.c b/kernel/fork.c
index 10917c3e1f03..eef15c93f6cf 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -2163,8 +2163,6 @@ static int __pidfd_prepare(struct pid *pid, unsigned int flags, struct file **re
* Allocate a new file that stashes @pid and reserve a new pidfd number in the
* caller's file descriptor table. The pidfd is reserved but not installed yet.
*
- * The helper verifies that @pid is used as a thread group leader.
- *
* If this function returns successfully the caller is responsible to either
* call fd_install() passing the returned pidfd and pidfd file as arguments in
* order to install the pidfd into its file descriptor table or they must use
@@ -2182,7 +2180,7 @@ static int __pidfd_prepare(struct pid *pid, unsigned int flags, struct file **re
*/
int pidfd_prepare(struct pid *pid, unsigned int flags, struct file **ret)
{
- if (!pid || !pid_has_task(pid, PIDTYPE_TGID))
+ if (!pid)
return -EINVAL;
return __pidfd_prepare(pid, flags, ret);
diff --git a/kernel/pid.c b/kernel/pid.c
index 6500ef956f2f..4806798022d9 100644
--- a/kernel/pid.c
+++ b/kernel/pid.c
@@ -552,11 +552,6 @@ struct pid *pidfd_get_pid(unsigned int fd, unsigned int *flags)
* Return the task associated with @pidfd. The function takes a reference on
* the returned task. The caller is responsible for releasing that reference.
*
- * Currently, the process identified by @pidfd is always a thread-group leader.
- * This restriction currently exists for all aspects of pidfds including pidfd
- * creation (CLONE_PIDFD cannot be used with CLONE_THREAD) and pidfd polling
- * (only supports thread group leaders).
- *
* Return: On success, the task_struct associated with the pidfd.
* On error, a negative errno number will be returned.
*/
@@ -615,11 +610,7 @@ int pidfd_create(struct pid *pid, unsigned int flags)
* @flags: flags to pass
*
* This creates a new pid file descriptor with the O_CLOEXEC flag set for
- * the process identified by @pid. Currently, the process identified by
- * @pid must be a thread-group leader. This restriction currently exists
- * for all aspects of pidfds including pidfd creation (CLONE_PIDFD cannot
- * be used with CLONE_THREAD) and pidfd polling (only supports thread group
- * leaders).
+ * the process identified by @pid.
*
* Return: On success, a cloexec pidfd is returned.
* On error, a negative errno number will be returned.
diff --git a/kernel/signal.c b/kernel/signal.c
index 47a7602dfe8d..7b3a1e147225 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -2028,7 +2028,7 @@ int send_sigqueue(struct sigqueue *q, struct pid *pid, enum pid_type type)
return ret;
}
-static void do_notify_pidfd(struct task_struct *task)
+void do_notify_pidfd(struct task_struct *task)
{
struct pid *pid;
@@ -2060,9 +2060,6 @@ bool do_notify_parent(struct task_struct *tsk, int sig)
WARN_ON_ONCE(!tsk->ptrace &&
(tsk->group_leader != tsk || !thread_group_empty(tsk)));
- /* Wake up all pidfd waiters */
- do_notify_pidfd(tsk);
-
if (sig != SIGCHLD) {
/*
* This is only possible if parent == real_parent.
base-commit: bee0e7762ad2c6025b9f5245c040fcc36ef2bde8
--
2.34.1
^ permalink raw reply related
* Re: [RFC PATCH 07/11] mm/mempolicy: add userland mempolicy arg structure
From: Arnd Bergmann @ 2023-12-07 15:43 UTC (permalink / raw)
To: Gregory Price
Cc: Gregory Price, linux-mm, jgroves, ravis.opensrc, sthanneeru,
emirakhur, Hasan.Maruf, linux-doc, linux-fsdevel, linux-api,
Linux-Arch, linux-kernel, Andrew Morton, Thomas Gleixner,
Andy Lutomirski, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Michal Hocko, Tejun Heo, ying.huang,
Jonathan Corbet, rakie.kim, hyeongtak.ji, honggyu.kim,
vtavarespetr, Peter Zijlstra, Frank van der Linden
In-Reply-To: <ZXHdhVeel1dOxlYJ@memverge.com>
On Thu, Dec 7, 2023, at 15:58, Gregory Price wrote:
> On Thu, Dec 07, 2023 at 08:13:22AM +0100, Arnd Bergmann wrote:
>> On Thu, Dec 7, 2023, at 01:27, Gregory Price wrote:
>>
>> Aside from this, you should avoid holes in the data structure.
>> On 64-bit architectures, the layout above has holes after
>> policy_node and after addr_node.
>>
>> Arnd
>
> doh, clearly i didn't stop to think about alignment. Good eye.
> I'll redo this with __u/s members and fix the holes.
>
> Didn't stop to think about compat pointers. I don't think the
> u64_to_user_ptr pattern is offensive, so i'll make that change.
> At least I don't see what the other options are beyond compat.
Ok, sounds good.
I see you already call wrappers for compat mode to convert
iovec and nodemask layouts for the indirect pointers, and they
look correct. If you wanted to do handle the compat syscalls
using the same entry point, you could add the same kind of
helper to copy the mempolicy args from user space with an
optional conversion, but not having to do this is clearly
easier.
Arnd
^ permalink raw reply
* Re: [PATCH v2] ELF: supply userspace with available page shifts (AT_PAGE_SHIFT_MASK)
From: Florian Weimer @ 2023-12-07 15:32 UTC (permalink / raw)
To: Alexey Dobriyan
Cc: Kees Cook, Andrew Morton, linux-kernel, linux-arch, linux-api,
x86
In-Reply-To: <4f5f29d4-9c50-453c-8ad3-03a92fed192e@p183>
* Alexey Dobriyan:
> On Wed, Dec 06, 2023 at 12:47:27PM -0800, Kees Cook wrote:
>> On Tue, Dec 05, 2023 at 07:01:34PM +0300, Alexey Dobriyan wrote:
>> > Report available page shifts in arch independent manner, so that
>> > userspace developers won't have to parse /proc/cpuinfo hunting
>> > for arch specific strings:
>> >
>> > Note!
>> >
>> > This is strictly for userspace, if some page size is shutdown due
>> > to kernel command line option or CPU bug workaround, than is must not
>> > be reported in aux vector!
>>
>> Given Florian in CC, I assume this is something glibc would like to be
>> using? Please mention this in the commit log.
>
> glibc can use it. Main user is libhugetlbfs, I guess:
>
> https://github.com/libhugetlbfs/libhugetlbfs/blob/master/hugeutils.c#L915
>
> Loop inside getauxval() can run faster than opendir().
Is libhugetlbfs still maintained? Last commit was three years ago?
Thanks,
Florian
^ permalink raw reply
* Re: [PATCH v2] ELF: supply userspace with available page shifts (AT_PAGE_SHIFT_MASK)
From: Alexey Dobriyan @ 2023-12-07 15:04 UTC (permalink / raw)
To: Kees Cook
Cc: Florian Weimer, Andrew Morton, linux-kernel, linux-arch,
linux-api, x86
In-Reply-To: <202312061308.630C56CCA@keescook>
On Wed, Dec 06, 2023 at 01:09:01PM -0800, Kees Cook wrote:
> On Wed, Dec 06, 2023 at 10:05:36PM +0100, Florian Weimer wrote:
> > * Kees Cook:
> >
> > > On Tue, Dec 05, 2023 at 07:01:34PM +0300, Alexey Dobriyan wrote:
> > >> Report available page shifts in arch independent manner, so that
> > >> userspace developers won't have to parse /proc/cpuinfo hunting
> > >> for arch specific strings:
> > >>
> > >> Note!
> > >>
> > >> This is strictly for userspace, if some page size is shutdown due
> > >> to kernel command line option or CPU bug workaround, than is must not
> > >> be reported in aux vector!
> > >
> > > Given Florian in CC, I assume this is something glibc would like to be
> > > using? Please mention this in the commit log.
> >
> > Nope, I just wrote a random drive-by comment on the first version.
>
> Ah, okay. Then Alexey, who do you expect to be the consumer of this new
> AT value?
libhugetlbfs and everyone who is using 2 MiB pages.
New code should look like this:
#ifndef AT_PAGE_SHIFT_MASK
#define AT_PAGE_SHIFT_MASK 29
#endif
unsigned long val = getauxval(AT_PAGE_SHIFT_MASK);
if (val) {
g_page_size_2mib = val & (1UL << 21);
return;
}
// old 2 MiB page detection code
It is few lines of fast code before code they're already using.
^ permalink raw reply
* Re: [RFC PATCH 07/11] mm/mempolicy: add userland mempolicy arg structure
From: Gregory Price @ 2023-12-07 14:58 UTC (permalink / raw)
To: Arnd Bergmann
Cc: Gregory Price, linux-mm, jgroves, ravis.opensrc, sthanneeru,
emirakhur, Hasan.Maruf, linux-doc, linux-fsdevel, linux-api,
Linux-Arch, linux-kernel, Andrew Morton, Thomas Gleixner,
Andy Lutomirski, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Michal Hocko, Tejun Heo, ying.huang,
Jonathan Corbet, rakie.kim, hyeongtak.ji, honggyu.kim,
vtavarespetr, Peter Zijlstra, Frank van der Linden
In-Reply-To: <67fab0f1-e326-4ad8-9def-4d2bd5489b33@app.fastmail.com>
On Thu, Dec 07, 2023 at 08:13:22AM +0100, Arnd Bergmann wrote:
> On Thu, Dec 7, 2023, at 01:27, Gregory Price wrote:
> > This patch adds the new user-api argument structure intended for
> > set_mempolicy2 and mbind2.
> >
> > struct mpol_args {
> > /* Basic mempolicy settings */
> > unsigned short mode;
> > unsigned short mode_flags;
> > unsigned long *pol_nodes;
> > unsigned long pol_maxnodes;
> >
> > /* get_mempolicy2: policy information (e.g. next interleave node) */
> > int policy_node;
> >
> > /* get_mempolicy2: memory range policy */
> > unsigned long addr;
> > int addr_node;
> >
> > /* all operations: policy home node */
> > unsigned long home_node;
> >
> > /* mbind2: address ranges to apply the policy */
> > const struct iovec __user *vec;
> > size_t vlen;
> > };
>
> This is not a great structure layout for a system call ABI,
> mostly because it requires adding a compat syscall handler
> to be usable from 32-bit tasks. It would be nice if this
> could be rewritten in a way that uses only fixed-length
> members (__u16, __u32, __aligned_u64), though that does
> require the use of u64_to_user_ptr() to replace the pointers
> and the reverse in userspace.
>
> Aside from this, you should avoid holes in the data structure.
> On 64-bit architectures, the layout above has holes after
> policy_node and after addr_node.
>
> Arnd
doh, clearly i didn't stop to think about alignment. Good eye.
I'll redo this with __u/s members and fix the holes.
Didn't stop to think about compat pointers. I don't think the
u64_to_user_ptr pattern is offensive, so i'll make that change.
At least I don't see what the other options are beyond compat.
Thanks
~Gregory
^ permalink raw reply
* Re: [PATCH v2] ELF: supply userspace with available page shifts (AT_PAGE_SHIFT_MASK)
From: Alexey Dobriyan @ 2023-12-07 14:57 UTC (permalink / raw)
To: Kees Cook
Cc: Andrew Morton, Florian Weimer, linux-kernel, linux-arch,
linux-api, x86
In-Reply-To: <202312061236.DE847C52AA@keescook>
On Wed, Dec 06, 2023 at 12:47:27PM -0800, Kees Cook wrote:
> On Tue, Dec 05, 2023 at 07:01:34PM +0300, Alexey Dobriyan wrote:
> > Report available page shifts in arch independent manner, so that
> > userspace developers won't have to parse /proc/cpuinfo hunting
> > for arch specific strings:
> >
> > Note!
> >
> > This is strictly for userspace, if some page size is shutdown due
> > to kernel command line option or CPU bug workaround, than is must not
> > be reported in aux vector!
>
> Given Florian in CC, I assume this is something glibc would like to be
> using? Please mention this in the commit log.
glibc can use it. Main user is libhugetlbfs, I guess:
https://github.com/libhugetlbfs/libhugetlbfs/blob/master/hugeutils.c#L915
Loop inside getauxval() can run faster than opendir().
> > x86_64 machine with 1 GiB pages:
> >
> > 00000030 06 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00
> > 00000040 1d 00 00 00 00 00 00 00 00 10 20 40 00 00 00 00
> >
> > x86_64 machine with 2 MiB pages only:
> >
> > 00000030 06 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00
> > 00000040 1d 00 00 00 00 00 00 00 00 10 20 00 00 00 00 00
> >
> > AT_PAGESZ is always 4096 which is not that interesting.
>
> That's not always true. For example, see arm64:
> arch/arm64/include/asm/elf.h:#define ELF_EXEC_PAGESIZE PAGE_SIZE
Yes, I'm x86_64 guy, AT_PAGESZ remark is about x86_64.
> I'm not actually sure why x86 forces it to 4096. I'd need to go look
> through the history there.
> > --- a/arch/x86/include/asm/elf.h
> > +++ b/arch/x86/include/asm/elf.h
> > @@ -358,6 +358,18 @@ else if (IS_ENABLED(CONFIG_IA32_EMULATION)) \
> >
> > #define COMPAT_ELF_ET_DYN_BASE (TASK_UNMAPPED_BASE + 0x1000000)
> >
> > +#define ARCH_AT_PAGE_SHIFT_MASK \
> > + do { \
> > + u32 val = 1 << 12; \
> > + if (boot_cpu_has(X86_FEATURE_PSE)) { \
> > + val |= 1 << 21; \
> > + } \
> > + if (boot_cpu_has(X86_FEATURE_GBPAGES)) { \
> > + val |= 1 << 30; \
> > + } \
> > + NEW_AUX_ENT(AT_PAGE_SHIFT_MASK, val); \
> > + } while (0)
> > +
> > #endif /* !CONFIG_X86_32 */
>
> Can't we have a generic ARCH_AT_PAGE_SHIFT_MASK too? Something like:
>
> #ifndef ARCH_AT_PAGE_SHIFT_MASK
> #define ARCH_AT_PAGE_SHIFT_MASK
> NEW_AUX_ENT(AT_PAGE_SHIFT_MASK, 1 << PAGE_SHIFT)
> #endif
>
> Or am I misunderstanding something here?
1) Arch maintainers can opt into this new way to report information at
their own pace.
2) AT_PAGE_SHIFT_MASK is about _all_ pagesizes supported by CPU.
Reporting just one is missing the point.
I'll clarify comment: mmap() support require many things including
tests for hugetlbfs being mounted, this is about CPU support.
> > --- a/fs/binfmt_elf.c
> > +++ b/fs/binfmt_elf.c
> > @@ -240,6 +240,9 @@ create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec,
> > #endif
> > NEW_AUX_ENT(AT_HWCAP, ELF_HWCAP);
> > NEW_AUX_ENT(AT_PAGESZ, ELF_EXEC_PAGESIZE);
> > +#ifdef ARCH_AT_PAGE_SHIFT_MASK
> > + ARCH_AT_PAGE_SHIFT_MASK;
> > +#endif
>
> That way we can avoid an #ifdef in the .c file.
That's a false economy. ifdefs aren't bad inherently.
When all archs implement AT_PAGE_SHIFT_MASK, ifdef will be removed.
> > --- a/include/uapi/linux/auxvec.h
> > +++ b/include/uapi/linux/auxvec.h
> > @@ -33,6 +33,20 @@
> > #define AT_RSEQ_FEATURE_SIZE 27 /* rseq supported feature size */
> > #define AT_RSEQ_ALIGN 28 /* rseq allocation alignment */
> >
> > +/*
> > + * Page sizes available for mmap(2) encoded as bitmask.
> > + *
> > + * Example: x86_64 system with pse, pdpe1gb /proc/cpuinfo flags reports
> > + * 4 KiB, 2 MiB and 1 GiB page support.
> > + *
> > + * $ hexdump -C /proc/self/auxv
>
> FWIW, a more readable form is: $ LD_SHOW_AUXV=1 /bin/true
OK. It doesn't show new values as text, but OK.
> > + * 00000030 06 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00
> > + * 00000040 1d 00 00 00 00 00 00 00 00 10 20 40 00 00 00 00
> > + *
> > + * For 2^64 hugepage support please contact your Universe sales representative.
> > + */
> > +#define AT_PAGE_SHIFT_MASK 29
>
> ... hmm, why is 29 unused?
>
> > +
> > #define AT_EXECFN 31 /* filename of program */
> >
> > #ifndef AT_MINSIGSTKSZ
>
> This will need a man page update for "getauxval" as well...
Hear, hear!
^ permalink raw reply
* Re: [RFC PATCH 07/11] mm/mempolicy: add userland mempolicy arg structure
From: Arnd Bergmann @ 2023-12-07 7:13 UTC (permalink / raw)
To: Gregory Price, linux-mm, jgroves, ravis.opensrc, sthanneeru,
emirakhur, Hasan.Maruf
Cc: linux-doc, linux-fsdevel, linux-api, Linux-Arch, linux-kernel,
Andrew Morton, Thomas Gleixner, Andy Lutomirski, Ingo Molnar,
Borislav Petkov, Dave Hansen, x86, H. Peter Anvin, Michal Hocko,
Tejun Heo, ying.huang, Gregory Price, Jonathan Corbet, rakie.kim,
hyeongtak.ji, honggyu.kim, vtavarespetr, Peter Zijlstra,
Frank van der Linden
In-Reply-To: <20231207002759.51418-8-gregory.price@memverge.com>
On Thu, Dec 7, 2023, at 01:27, Gregory Price wrote:
> This patch adds the new user-api argument structure intended for
> set_mempolicy2 and mbind2.
>
> struct mpol_args {
> /* Basic mempolicy settings */
> unsigned short mode;
> unsigned short mode_flags;
> unsigned long *pol_nodes;
> unsigned long pol_maxnodes;
>
> /* get_mempolicy2: policy information (e.g. next interleave node) */
> int policy_node;
>
> /* get_mempolicy2: memory range policy */
> unsigned long addr;
> int addr_node;
>
> /* all operations: policy home node */
> unsigned long home_node;
>
> /* mbind2: address ranges to apply the policy */
> const struct iovec __user *vec;
> size_t vlen;
> };
This is not a great structure layout for a system call ABI,
mostly because it requires adding a compat syscall handler
to be usable from 32-bit tasks. It would be nice if this
could be rewritten in a way that uses only fixed-length
members (__u16, __u32, __aligned_u64), though that does
require the use of u64_to_user_ptr() to replace the pointers
and the reverse in userspace.
Aside from this, you should avoid holes in the data structure.
On 64-bit architectures, the layout above has holes after
policy_node and after addr_node.
Arnd
^ permalink raw reply
* [RFC PATCH 11/11] mm/mempolicy: extend set_mempolicy2 and mbind2 to support weighted interleave
From: Gregory Price @ 2023-12-07 0:27 UTC (permalink / raw)
To: linux-mm, jgroves, ravis.opensrc, sthanneeru, emirakhur,
Hasan.Maruf
Cc: linux-doc, linux-fsdevel, linux-api, linux-arch, linux-kernel,
akpm, arnd, tglx, luto, mingo, bp, dave.hansen, x86, hpa, mhocko,
tj, ying.huang, gregory.price, corbet, rakie.kim, hyeongtak.ji,
honggyu.kim, vtavarespetr, peterz, Gregory Price
In-Reply-To: <20231207002759.51418-1-gregory.price@memverge.com>
From: Gregory Price <gregory@gregoryprice.net>
Extend set_mempolicy2 and mbind2 to support weighted interleave, and
demonstrate the extensibility of the mpol_args structure.
To support weighted interleave we add interleave weight fields to the
following structures:
Kernel Internal: (include/linux/mempolicy.h)
struct mempolicy {
/* task-local weights to apply to weighted interleave */
unsigned char weights[MAX_NUMNODES];
}
struct mempolicy_args {
/* Optional: interleave weights for MPOL_WEIGHTED_INTERLEAVE */
unsigned char *il_weights; /* of size MAX_NUMNODES */
}
UAPI: (/include/uapi/linux/mempolicy.h)
struct mpol_args {
/* Optional: interleave weights for MPOL_WEIGHTED_INTERLEAVE */
unsigned char *il_weights; /* of size pol_max_nodes */
}
The task-local weights are a single, one-dimensional array of weights
that apply to all possible nodes on the system. If a node is set in
the mempolicy nodemask, the weight in `il_weights` must be >= 1,
otherwise set_mempolicy2() will return -EINVAL. If a node is not
set in pol_nodemask, the weight will default to `1` in the task policy.
The default value of `1` is required to handle the situation where a
task migrates to a set of nodes for which weights were not set (up to
and including the local numa node). For example, a migrated task whose
nodemask changes entirely will have all its weights defaulted back
to `1`, or if the nodemask changes to include a mix of nodes that
were not previously accounted for - the weighted interleave may be
suboptimal.
If migrations are expected, a task should prefer not to use task-local
interleave weights, and instead utilize the global settings for natural
re-weighting on migration.
To support global vs local weighting, we add the kernel-internal flag:
MPOL_F_GWEIGHT (1 << 5) /* Utilize global weights */
This flag is set when il_weights is omitted by set_mempolicy2(), or
when MPOL_WEIGHTED_INTERLEAVE is set by set_mempolicy(). This internal
mode_flag dictates whether global weights or task-local weights are
utilized by the the various weighted interleave functions:
* weighted_interleave_nodes
* weighted_interleave_nid
* alloc_pages_bulk_array_weighted_interleave
if (pol->flags & MPOL_F_GWEIGHT)
pol_weights = iw_table[numa_node_id()].weights;
else
pol_weights = pol->wil.weights;
To simplify creations and duplication of mempolicies, the weights are
added as a structure directly within mempolicy. This allows the
existing logic in __mpol_dup to copy the weights without additional
allocations:
if (old == current->mempolicy) {
task_lock(current);
*new = *old;
task_unlock(current);
} else
*new = *old
Suggested-by: Rakie Kim <rakie.kim@sk.com>
Suggested-by: Hyeongtak Ji <hyeongtak.ji@sk.com>
Suggested-by: Honggyu Kim <honggyu.kim@sk.com>
Suggested-by: Vinicius Tavares Petrucci <vtavarespetr@micron.com>
Signed-off-by: Gregory Price <gregory.price@memverge.com>
Co-developed-by: Rakie Kim <rakie.kim@sk.com>
Signed-off-by: Rakie Kim <rakie.kim@sk.com>
Co-developed-by: Hyeongtak Ji <hyeongtak.ji@sk.com>
Signed-off-by: Hyeongtak Ji <hyeongtak.ji@sk.com>
Co-developed-by: Honggyu Kim <honggyu.kim@sk.com>
Signed-off-by: Honggyu Kim <honggyu.kim@sk.com>
Co-developed-by: Vinicius Tavares Petrucci <vtavarespetr@micron.com>
Signed-off-by: Vinicius Tavares Petrucci <vtavarespetr@micron.com>
---
.../admin-guide/mm/numa_memory_policy.rst | 13 ++-
include/linux/mempolicy.h | 2 +
include/uapi/linux/mempolicy.h | 3 +
mm/mempolicy.c | 87 ++++++++++++++++++-
4 files changed, 100 insertions(+), 5 deletions(-)
diff --git a/Documentation/admin-guide/mm/numa_memory_policy.rst b/Documentation/admin-guide/mm/numa_memory_policy.rst
index 72ab21e24ec2..f3a9dcbaa7ed 100644
--- a/Documentation/admin-guide/mm/numa_memory_policy.rst
+++ b/Documentation/admin-guide/mm/numa_memory_policy.rst
@@ -254,7 +254,8 @@ MPOL_WEIGHTED_INTERLEAVE
This mode operates the same as MPOL_INTERLEAVE, except that
interleaving behavior is executed based on weights set in
/sys/kernel/mm/mempolicy/weighted_interleave/
- rather than simple round-robin interleave (which is the default).
+ when configured to utilize global weights, or based on task-local
+ weights configured with set_mempolicy2(2) or mbind2(2).
When utilizing global weights from the sysfs interface,
weights are applied in a src-node relative manner. For example
@@ -267,6 +268,13 @@ MPOL_WEIGHTED_INTERLEAVE
cgroup initiated migrations) to re-weight for the optimal
distribution of bandwidth.
+ When utilizing task-local weights, weights are not rebalanced
+ in the event of a task migration. If a weight has not been
+ explicitly set for a node set in the new nodemask, the
+ value of that weight defaults to "1". For this reason, if
+ migrations are expected or possible, users should consider
+ utilizing global interleave weights.
+
NUMA memory policy supports the following optional mode flags:
MPOL_F_STATIC_NODES
@@ -533,6 +541,9 @@ Extended Mempolicy Arguments::
/* mbind2: address ranges to apply the policy */
struct iovec *vec;
size_t vlen;
+
+ /* weighted interleave settings */
+ unsigned char *il_weights; /* of size pol_maxnodes */
};
The extended mempolicy argument structure is defined to allow the mempolicy
diff --git a/include/linux/mempolicy.h b/include/linux/mempolicy.h
index 117c5395c6eb..c78874bd84dd 100644
--- a/include/linux/mempolicy.h
+++ b/include/linux/mempolicy.h
@@ -58,6 +58,7 @@ struct mempolicy {
/* Weighted interleave settings */
struct {
unsigned char cur_weight;
+ unsigned char weights[MAX_NUMNODES];
} wil;
};
@@ -73,6 +74,7 @@ struct mempolicy_args {
unsigned long addr; /* get: vma address */
int addr_node; /* get: node the address belongs to */
int home_node; /* mbind: use MPOL_MF_HOME_NODE */
+ unsigned char *il_weights; /* for mode MPOL_WEIGHTED_INTERLEAVE */
};
/*
diff --git a/include/uapi/linux/mempolicy.h b/include/uapi/linux/mempolicy.h
index 3e463442fe28..c2f229037be3 100644
--- a/include/uapi/linux/mempolicy.h
+++ b/include/uapi/linux/mempolicy.h
@@ -43,6 +43,8 @@ struct mpol_args {
/* mbind2: address ranges to apply the policy */
struct iovec *vec;
size_t vlen;
+ /* weighted interleave settings */
+ unsigned char *il_weights; /* of size pol_maxnodes */
};
/* Flags for set_mempolicy */
@@ -83,6 +85,7 @@ struct mpol_args {
#define MPOL_F_SHARED (1 << 0) /* identify shared policies */
#define MPOL_F_MOF (1 << 3) /* this policy wants migrate on fault */
#define MPOL_F_MORON (1 << 4) /* Migrate On protnone Reference On Node */
+#define MPOL_F_GWEIGHT (1 << 5) /* Utilize global weights */
/*
* These bit locations are exposed in the vm.zone_reclaim_mode sysctl
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index c203cea52ce9..7273bb9540fa 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -274,6 +274,7 @@ static struct mempolicy *mpol_new(struct mempolicy_args *args)
unsigned short mode = args->mode;
unsigned short flags = args->mode_flags;
nodemask_t *nodes = args->policy_nodes;
+ int node;
if (mode == MPOL_DEFAULT) {
if (nodes && !nodes_empty(*nodes))
@@ -300,6 +301,19 @@ static struct mempolicy *mpol_new(struct mempolicy_args *args)
(flags & MPOL_F_STATIC_NODES) ||
(flags & MPOL_F_RELATIVE_NODES))
return ERR_PTR(-EINVAL);
+ } else if (mode == MPOL_WEIGHTED_INTERLEAVE) {
+ /* weighted interleave requires a nodemask and weights > 0 */
+ if (nodes_empty(*nodes))
+ return ERR_PTR(-EINVAL);
+ if (args->il_weights) {
+ node = first_node(*nodes);
+ while (node != MAX_NUMNODES) {
+ if (!args->il_weights[node])
+ return ERR_PTR(-EINVAL);
+ node = next_node(node, *nodes);
+ }
+ } else if (!(args->mode_flags & MPOL_F_GWEIGHT))
+ return ERR_PTR(-EINVAL);
} else if (nodes_empty(*nodes))
return ERR_PTR(-EINVAL);
@@ -312,6 +326,16 @@ static struct mempolicy *mpol_new(struct mempolicy_args *args)
policy->home_node = NUMA_NO_NODE;
policy->wil.cur_weight = 0;
policy->home_node = args->home_node;
+ if (policy->mode == MPOL_WEIGHTED_INTERLEAVE && args->il_weights) {
+ policy->wil.cur_weight = 0;
+ /* Minimum weight value is always 1 */
+ memset(policy->wil.weights, 1, MAX_NUMNODES);
+ node = first_node(*nodes);
+ while (node != MAX_NUMNODES) {
+ policy->wil.weights[node] = args->il_weights[node];
+ node = next_node(node, *nodes);
+ }
+ }
return policy;
}
@@ -1612,6 +1636,7 @@ SYSCALL_DEFINE3(mbind2, struct mpol_args __user *, uargs, size_t, usize,
struct iovec iovstack[UIO_FASTIOV];
struct iovec *iov = iovstack;
struct iov_iter iter;
+ unsigned char weights[MAX_NUMNODES];
int err;
err = copy_struct_from_user(&kargs, sizeof(kargs), uargs, usize);
@@ -1648,6 +1673,19 @@ SYSCALL_DEFINE3(mbind2, struct mpol_args __user *, uargs, size_t, usize,
} else
margs.policy_nodes = NULL;
+ if (kargs.mode == MPOL_WEIGHTED_INTERLEAVE) {
+ err = copy_struct_from_user(&weights,
+ sizeof(weights),
+ &kargs.il_weights,
+ kargs.pol_maxnodes);
+ if (err)
+ return err;
+ margs.il_weights = weights;
+ } else {
+ margs.il_weights = NULL;
+ flags |= MPOL_F_GWEIGHT;
+ }
+
/* For each address range in vector, do_mbind */
err = import_iovec(ITER_DEST, kargs.vec, kargs.vlen,
ARRAY_SIZE(iovstack), &iov, &iter);
@@ -1686,6 +1724,9 @@ static long kernel_set_mempolicy(int mode, const unsigned long __user *nmask,
if (err)
return err;
+ if (mode & MPOL_WEIGHTED_INTERLEAVE)
+ mode_flags |= MPOL_F_GWEIGHT;
+
memset(&args, 0, sizeof(args));
args.mode = lmode;
args.mode_flags = mode_flags;
@@ -1708,6 +1749,7 @@ SYSCALL_DEFINE3(set_mempolicy2, struct mpol_args __user *, uargs, size_t, usize,
struct mempolicy_args margs;
int err;
nodemask_t policy_nodemask;
+ unsigned char weights[MAX_NUMNODES];
if (flags)
return -EINVAL;
@@ -1732,6 +1774,19 @@ SYSCALL_DEFINE3(set_mempolicy2, struct mpol_args __user *, uargs, size_t, usize,
} else
margs.policy_nodes = NULL;
+ if (kargs.mode == MPOL_WEIGHTED_INTERLEAVE && kargs.il_weights) {
+ err = copy_struct_from_user(weights,
+ sizeof(weights),
+ kargs.il_weights,
+ kargs.pol_maxnodes);
+ if (err)
+ return err;
+ margs.il_weights = weights;
+ } else {
+ margs.il_weights = NULL;
+ flags |= MPOL_F_GWEIGHT;
+ }
+
return do_set_mempolicy(&margs);
}
@@ -2081,16 +2136,22 @@ static unsigned int weighted_interleave_nodes(struct mempolicy *policy)
{
unsigned int next;
struct task_struct *me = current;
+ unsigned char *weights;
if (policy->wil.cur_weight > 0) {
policy->wil.cur_weight--;
return me->il_prev;
}
+ if (policy->flags & MPOL_F_GWEIGHT)
+ weights = iw_table[numa_node_id()].weights;
+ else
+ weights = policy->wil.weights;
+
next = next_node_in(me->il_prev, policy->nodes);
if (next < MAX_NUMNODES) {
me->il_prev = next;
- policy->wil.cur_weight = iw_table[numa_node_id()].weights[next];
+ policy->wil.cur_weight = weights[next];
}
return next;
}
@@ -2160,15 +2221,21 @@ static unsigned int weighted_interleave_nid(struct mempolicy *pol, pgoff_t ilx)
{
nodemask_t nodemask = pol->nodes;
unsigned int target, weight_total = 0;
- int nid, local_node = numa_node_id();
+ int nid;
+ unsigned char *pol_weights;
unsigned char weights[MAX_NUMNODES];
unsigned char weight;
barrier();
+ if (pol->flags & MPOL_F_GWEIGHT)
+ pol_weights = iw_table[numa_node_id()].weights;
+ else
+ pol_weights = pol->wil.weights;
+
/* Collect weights and save them on stack so they don't change */
for_each_node_mask(nid, nodemask) {
- weight = iw_table[local_node].weights[nid];
+ weight = pol_weights[nid];
weight_total += weight;
weights[nid] = weight;
}
@@ -2564,6 +2631,7 @@ static unsigned long alloc_pages_bulk_array_weighted_interleave(gfp_t gfp,
unsigned long nr_allocated;
unsigned long rounds;
unsigned long node_pages, delta;
+ unsigned char *pol_weights;
unsigned char weight;
unsigned char weights[MAX_NUMNODES];
unsigned int weight_total;
@@ -2576,9 +2644,14 @@ static unsigned long alloc_pages_bulk_array_weighted_interleave(gfp_t gfp,
nnodes = nodes_weight(nodes);
+ if (pol->flags & MPOL_F_GWEIGHT)
+ pol_weights = iw_table[numa_node_id()].weights;
+ else
+ pol_weights = pol->wil.weights;
+
/* Collect weights and save them on stack so they don't change */
for_each_node_mask(node, nodes) {
- weight = iw_table[numa_node_id()].weights[node];
+ weight = pol_weights[node];
weight_total += weight;
weights[node] = weight;
}
@@ -3095,6 +3168,7 @@ void mpol_shared_policy_init(struct shared_policy *sp, struct mempolicy *mpol)
{
int ret;
struct mempolicy_args margs;
+ unsigned char weights[MAX_NUMNODES];
sp->root = RB_ROOT; /* empty tree == default mempolicy */
rwlock_init(&sp->lock);
@@ -3112,6 +3186,11 @@ void mpol_shared_policy_init(struct shared_policy *sp, struct mempolicy *mpol)
margs.mode_flags = mpol->flags;
margs.policy_nodes = &mpol->w.user_nodemask;
margs.home_node = NUMA_NO_NODE;
+ if (margs.mode == MPOL_WEIGHTED_INTERLEAVE &&
+ !(margs.mode_flags & MPOL_F_GWEIGHT)) {
+ memcpy(weights, mpol->wil.weights, sizeof(weights));
+ margs.il_weights = weights;
+ }
/* contextualize the tmpfs mount point mempolicy to this file */
npol = mpol_new(&margs);
--
2.39.1
^ permalink raw reply related
* [RFC PATCH 10/11] mm/mempolicy: add the mbind2 syscall
From: Gregory Price @ 2023-12-07 0:27 UTC (permalink / raw)
To: linux-mm, jgroves, ravis.opensrc, sthanneeru, emirakhur,
Hasan.Maruf
Cc: linux-doc, linux-fsdevel, linux-api, linux-arch, linux-kernel,
akpm, arnd, tglx, luto, mingo, bp, dave.hansen, x86, hpa, mhocko,
tj, ying.huang, gregory.price, corbet, rakie.kim, hyeongtak.ji,
honggyu.kim, vtavarespetr, peterz, Michal Hocko,
Frank van der Linden
In-Reply-To: <20231207002759.51418-1-gregory.price@memverge.com>
mbind2 is an extensible mbind interface which allows a user to
set the mempolicy for one or more address ranges.
Defined as:
mbind2(struct mpol_args *args, size_t size, unsigned long flags)
Input values include the following fields of mpl_args:
mode: The MPOL_* policy (DEFAULT, INTERLEAVE, etc.)
mode_flags: The MPOL_F_* flags that were previously passed in or'd
into the mode. This was split to hopefully allow future
extensions additional mode/flag space.
pol_nodes: the nodemask to apply for the memory policy
pol_maxnodes: The max number of nodes described by pol_nodes
home_node: if MPOL_MF_HOME_NODE, set home node of policy to this
vec: the vector of (address, len) memory ranges to operate on
vlen: the number of entries in vec
The semantics are otherwise the same as mbind(), except that
the home_node can be set, and all address ranges defined by
vec/vlen will be operated on.
Valid flags for mbind2 include the same flags as mbind, plus
MPOL_MF_HOME_NODE, which informs the syscall to utilize the value
of mpol_args->home_node to set the mempolicy home node.
Suggested-by: Michal Hocko <mhocko@suse.com>
Suggested-by: Frank van der Linden <fvdl@google.com>
Suggested-by: Vinicius Tavares Petrucci <vtavarespetr@micron.com>
Suggested-by: Rakie Kim <rakie.kim@sk.com>
Suggested-by: Hyeongtak Ji <hyeongtak.ji@sk.com>
Suggested-by: Honggyu Kim <honggyu.kim@sk.com>
Signed-off-by: Gregory Price <gregory.price@memverge.com>
Co-developed-by: Vinicius Tavares Petrucci <vtavarespetr@micron.com>
---
.../admin-guide/mm/numa_memory_policy.rst | 12 +++-
arch/alpha/kernel/syscalls/syscall.tbl | 1 +
arch/arm/tools/syscall.tbl | 1 +
arch/m68k/kernel/syscalls/syscall.tbl | 1 +
arch/microblaze/kernel/syscalls/syscall.tbl | 1 +
arch/mips/kernel/syscalls/syscall_n32.tbl | 1 +
arch/mips/kernel/syscalls/syscall_o32.tbl | 1 +
arch/parisc/kernel/syscalls/syscall.tbl | 1 +
arch/powerpc/kernel/syscalls/syscall.tbl | 1 +
arch/s390/kernel/syscalls/syscall.tbl | 1 +
arch/sh/kernel/syscalls/syscall.tbl | 1 +
arch/sparc/kernel/syscalls/syscall.tbl | 1 +
arch/x86/entry/syscalls/syscall_32.tbl | 1 +
arch/x86/entry/syscalls/syscall_64.tbl | 1 +
arch/xtensa/kernel/syscalls/syscall.tbl | 1 +
include/linux/syscalls.h | 2 +
include/uapi/asm-generic/unistd.h | 4 +-
include/uapi/linux/mempolicy.h | 5 +-
mm/mempolicy.c | 65 +++++++++++++++++++
19 files changed, 98 insertions(+), 4 deletions(-)
diff --git a/Documentation/admin-guide/mm/numa_memory_policy.rst b/Documentation/admin-guide/mm/numa_memory_policy.rst
index 82cdb765dd58..72ab21e24ec2 100644
--- a/Documentation/admin-guide/mm/numa_memory_policy.rst
+++ b/Documentation/admin-guide/mm/numa_memory_policy.rst
@@ -481,12 +481,18 @@ Install VMA/Shared Policy for a Range of Task's Address Space::
long mbind(void *start, unsigned long len, int mode,
const unsigned long *nmask, unsigned long maxnode,
unsigned flags);
+ long mbind2(struct mpol_args args, size_t size,
+ unsigned long flags);
mbind() installs the policy specified by (mode, nmask, maxnodes) as a
VMA policy for the range of the calling task's address space specified
by the 'start' and 'len' arguments. Additional actions may be
requested via the 'flags' argument.
+mbind2() is an extended version of mbind() capable of operating on multiple
+memory ranges in one scall, and which is capable of setting the home node
+for the memory policy without an additional call to set_mempolicy_home_node()
+
See the mbind(2) man page for more details.
Set home node for a Range of Task's Address Spacec::
@@ -502,6 +508,9 @@ closest to which page allocation will come from. Specifying the home node overri
the default allocation policy to allocate memory close to the local node for an
executing CPU.
+mbind2() also provides a way for the home node to be set at the time the
+mempolicy is set. See the mbind(2) man page for more details.
+
Extended Mempolicy Arguments::
struct mpol_args {
@@ -529,7 +538,8 @@ Extended Mempolicy Arguments::
The extended mempolicy argument structure is defined to allow the mempolicy
interfaces future extensibility without the need for additional system calls.
-Extended interfaces (set_mempolicy2 and get_mempolicy2) use this structure.
+Extended interfaces (set_mempolicy2, get_mempolicy2, and mbind2) use this
+this argument structure.
The core arguments (mode, mode_flags, pol_nodes, and pol_maxnodes) apply to
all interfaces relative to their non-extended counterparts. Each additional
diff --git a/arch/alpha/kernel/syscalls/syscall.tbl b/arch/alpha/kernel/syscalls/syscall.tbl
index 0301a8b0a262..e8239293c35a 100644
--- a/arch/alpha/kernel/syscalls/syscall.tbl
+++ b/arch/alpha/kernel/syscalls/syscall.tbl
@@ -498,3 +498,4 @@
566 common futex_requeue sys_futex_requeue
567 common set_mempolicy2 sys_set_mempolicy2
568 common get_mempolicy2 sys_get_mempolicy2
+569 common mbind2 sys_mbind2
diff --git a/arch/arm/tools/syscall.tbl b/arch/arm/tools/syscall.tbl
index 771a33446e8e..a3f39750257a 100644
--- a/arch/arm/tools/syscall.tbl
+++ b/arch/arm/tools/syscall.tbl
@@ -472,3 +472,4 @@
456 common futex_requeue sys_futex_requeue
457 common set_mempolicy2 sys_set_mempolicy2
458 common get_mempolicy2 sys_get_mempolicy2
+459 common mbind2 sys_mbind2
diff --git a/arch/m68k/kernel/syscalls/syscall.tbl b/arch/m68k/kernel/syscalls/syscall.tbl
index 048a409e684c..9a12dface18e 100644
--- a/arch/m68k/kernel/syscalls/syscall.tbl
+++ b/arch/m68k/kernel/syscalls/syscall.tbl
@@ -458,3 +458,4 @@
456 common futex_requeue sys_futex_requeue
457 common set_mempolicy2 sys_set_mempolicy2
458 common get_mempolicy2 sys_get_mempolicy2
+459 common mbind2 sys_mbind2
diff --git a/arch/microblaze/kernel/syscalls/syscall.tbl b/arch/microblaze/kernel/syscalls/syscall.tbl
index 327b01bd6793..6cb740123137 100644
--- a/arch/microblaze/kernel/syscalls/syscall.tbl
+++ b/arch/microblaze/kernel/syscalls/syscall.tbl
@@ -464,3 +464,4 @@
456 common futex_requeue sys_futex_requeue
457 common set_mempolicy2 sys_set_mempolicy2
458 common get_mempolicy2 sys_get_mempolicy2
+459 common mbind2 sys_mbind2
diff --git a/arch/mips/kernel/syscalls/syscall_n32.tbl b/arch/mips/kernel/syscalls/syscall_n32.tbl
index 921d58e1da23..52cf720f8ae2 100644
--- a/arch/mips/kernel/syscalls/syscall_n32.tbl
+++ b/arch/mips/kernel/syscalls/syscall_n32.tbl
@@ -397,3 +397,4 @@
456 n32 futex_requeue sys_futex_requeue
457 n32 set_mempolicy2 sys_set_mempolicy2
458 n32 get_mempolicy2 sys_get_mempolicy2
+459 n32 mbind2 sys_mbind2
diff --git a/arch/mips/kernel/syscalls/syscall_o32.tbl b/arch/mips/kernel/syscalls/syscall_o32.tbl
index 9271c83c9993..fd37c5301a48 100644
--- a/arch/mips/kernel/syscalls/syscall_o32.tbl
+++ b/arch/mips/kernel/syscalls/syscall_o32.tbl
@@ -446,3 +446,4 @@
456 o32 futex_requeue sys_futex_requeue
457 o32 set_mempolicy2 sys_set_mempolicy2
458 o32 get_mempolicy2 sys_get_mempolicy2
+459 o32 mbind2 sys_mbind2
diff --git a/arch/parisc/kernel/syscalls/syscall.tbl b/arch/parisc/kernel/syscalls/syscall.tbl
index 0654f3f89fc7..fcd67bc405b1 100644
--- a/arch/parisc/kernel/syscalls/syscall.tbl
+++ b/arch/parisc/kernel/syscalls/syscall.tbl
@@ -457,3 +457,4 @@
456 common futex_requeue sys_futex_requeue
457 common set_mempolicy2 sys_set_mempolicy2
458 common get_mempolicy2 sys_get_mempolicy2
+459 common mbind2 sys_mbind2
diff --git a/arch/powerpc/kernel/syscalls/syscall.tbl b/arch/powerpc/kernel/syscalls/syscall.tbl
index ac11d2064e7a..89715417014c 100644
--- a/arch/powerpc/kernel/syscalls/syscall.tbl
+++ b/arch/powerpc/kernel/syscalls/syscall.tbl
@@ -545,3 +545,4 @@
456 common futex_requeue sys_futex_requeue
457 common set_mempolicy2 sys_set_mempolicy2
458 common get_mempolicy2 sys_get_mempolicy2
+459 common mbind2 sys_mbind2
diff --git a/arch/s390/kernel/syscalls/syscall.tbl b/arch/s390/kernel/syscalls/syscall.tbl
index 1cdcafe1ccca..c8304e0d0aa7 100644
--- a/arch/s390/kernel/syscalls/syscall.tbl
+++ b/arch/s390/kernel/syscalls/syscall.tbl
@@ -461,3 +461,4 @@
456 common futex_requeue sys_futex_requeue sys_futex_requeue
457 common set_mempolicy2 sys_set_mempolicy2 sys_set_mempolicy2
458 common get_mempolicy2 sys_get_mempolicy2 sys_get_mempolicy2
+459 common mbind2 sys_mbind2 sys_mbind2
diff --git a/arch/sh/kernel/syscalls/syscall.tbl b/arch/sh/kernel/syscalls/syscall.tbl
index f71742024c29..e5c51b6c367f 100644
--- a/arch/sh/kernel/syscalls/syscall.tbl
+++ b/arch/sh/kernel/syscalls/syscall.tbl
@@ -461,3 +461,4 @@
456 common futex_requeue sys_futex_requeue
457 common set_mempolicy2 sys_set_mempolicy2
458 common get_mempolicy2 sys_get_mempolicy2
+459 common mbind2 sys_mbind2
diff --git a/arch/sparc/kernel/syscalls/syscall.tbl b/arch/sparc/kernel/syscalls/syscall.tbl
index 2fbf5dbe0620..74527f585500 100644
--- a/arch/sparc/kernel/syscalls/syscall.tbl
+++ b/arch/sparc/kernel/syscalls/syscall.tbl
@@ -504,3 +504,4 @@
456 common futex_requeue sys_futex_requeue
457 common set_mempolicy2 sys_set_mempolicy2
458 common get_mempolicy2 sys_get_mempolicy2
+459 common mbind2 sys_mbind2
diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
index 0af813b9a118..be2e2aa17dd8 100644
--- a/arch/x86/entry/syscalls/syscall_32.tbl
+++ b/arch/x86/entry/syscalls/syscall_32.tbl
@@ -463,3 +463,4 @@
456 i386 futex_requeue sys_futex_requeue
457 i386 set_mempolicy2 sys_set_mempolicy2
458 i386 get_mempolicy2 sys_get_mempolicy2
+459 i386 mbind2 sys_mbind2
diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
index 0b777876fc15..6e2347eb8773 100644
--- a/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/arch/x86/entry/syscalls/syscall_64.tbl
@@ -380,6 +380,7 @@
456 common futex_requeue sys_futex_requeue
457 common set_mempolicy2 sys_set_mempolicy2
458 common get_mempolicy2 sys_get_mempolicy2
+459 common mbind2 sys_mbind2
#
# Due to a historical design error, certain syscalls are numbered differently
diff --git a/arch/xtensa/kernel/syscalls/syscall.tbl b/arch/xtensa/kernel/syscalls/syscall.tbl
index 4536c9a4227d..f00a21317dc0 100644
--- a/arch/xtensa/kernel/syscalls/syscall.tbl
+++ b/arch/xtensa/kernel/syscalls/syscall.tbl
@@ -429,3 +429,4 @@
456 common futex_requeue sys_futex_requeue
457 common set_mempolicy2 sys_set_mempolicy2
458 common get_mempolicy2 sys_get_mempolicy2
+459 common mbind2 sys_mbind2
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 774512b7934e..a49a67e496bc 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -816,6 +816,8 @@ asmlinkage long sys_mbind(unsigned long start, unsigned long len,
const unsigned long __user *nmask,
unsigned long maxnode,
unsigned flags);
+asmlinkage long sys_mbind2(struct mpol_args *args, size_t size,
+ unsigned long flags);
asmlinkage long sys_get_mempolicy(int __user *policy,
unsigned long __user *nmask,
unsigned long maxnode,
diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h
index 719accc731db..cd31599bb9cc 100644
--- a/include/uapi/asm-generic/unistd.h
+++ b/include/uapi/asm-generic/unistd.h
@@ -832,9 +832,11 @@ __SYSCALL(__NR_futex_requeue, sys_futex_requeue)
__SYSCALL(__NR_set_mempolicy2, sys_set_mempolicy2)
#define __NR_get_mempolicy2 458
__SYSCALL(__NR_get_mempolicy2, sys_get_mempolicy2)
+#define __NR_mbind2 459
+__SYSCALL(__NR_mbind2, sys_mbind2)
#undef __NR_syscalls
-#define __NR_syscalls 459
+#define __NR_syscalls 460
/*
* 32 bit systems traditionally used different
diff --git a/include/uapi/linux/mempolicy.h b/include/uapi/linux/mempolicy.h
index e6b50903047c..3e463442fe28 100644
--- a/include/uapi/linux/mempolicy.h
+++ b/include/uapi/linux/mempolicy.h
@@ -62,13 +62,14 @@ struct mpol_args {
#define MPOL_F_ADDR (1<<1) /* look up vma using address */
#define MPOL_F_MEMS_ALLOWED (1<<2) /* return allowed memories */
-/* Flags for mbind */
+/* Flags for mbind/mbind2 */
#define MPOL_MF_STRICT (1<<0) /* Verify existing pages in the mapping */
#define MPOL_MF_MOVE (1<<1) /* Move pages owned by this process to conform
to policy */
#define MPOL_MF_MOVE_ALL (1<<2) /* Move every page to conform to policy */
#define MPOL_MF_LAZY (1<<3) /* UNSUPPORTED FLAG: Lazy migrate on fault */
-#define MPOL_MF_INTERNAL (1<<4) /* Internal flags start here */
+#define MPOL_MF_HOME_NODE (1<<4) /* mbind2: set home node */
+#define MPOL_MF_INTERNAL (1<<5) /* Internal flags start here */
#define MPOL_MF_VALID (MPOL_MF_STRICT | \
MPOL_MF_MOVE | \
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index d1d10b2746e3..c203cea52ce9 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -1603,6 +1603,71 @@ SYSCALL_DEFINE6(mbind, unsigned long, start, unsigned long, len,
return kernel_mbind(start, len, mode, nmask, maxnode, flags);
}
+SYSCALL_DEFINE3(mbind2, struct mpol_args __user *, uargs, size_t, usize,
+ unsigned long, flags)
+{
+ struct mpol_args kargs;
+ struct mempolicy_args margs;
+ nodemask_t policy_nodes;
+ struct iovec iovstack[UIO_FASTIOV];
+ struct iovec *iov = iovstack;
+ struct iov_iter iter;
+ int err;
+
+ err = copy_struct_from_user(&kargs, sizeof(kargs), uargs, usize);
+ if (err)
+ return -EINVAL;
+
+ err = validate_mpol_flags(kargs.mode, &kargs.mode_flags);
+ if (err)
+ return err;
+
+ if (!kargs.vec || !kargs.vlen)
+ return -EINVAL;
+
+ margs.mode = kargs.mode;
+ margs.mode_flags = kargs.mode_flags;
+ margs.addr = kargs.addr;
+
+ /* if home node given, validate it is online */
+ if (flags & MPOL_MF_HOME_NODE) {
+ if ((kargs.home_node >= MAX_NUMNODES) ||
+ !node_online(kargs.home_node))
+ return -EINVAL;
+ margs.home_node = kargs.home_node;
+ } else
+ margs.home_node = NUMA_NO_NODE;
+ flags &= ~MPOL_MF_HOME_NODE;
+
+ if (kargs.pol_nodes) {
+ err = get_nodes(&policy_nodes, kargs.pol_nodes,
+ kargs.pol_maxnodes);
+ if (err)
+ return err;
+ margs.policy_nodes = &policy_nodes;
+ } else
+ margs.policy_nodes = NULL;
+
+ /* For each address range in vector, do_mbind */
+ err = import_iovec(ITER_DEST, kargs.vec, kargs.vlen,
+ ARRAY_SIZE(iovstack), &iov, &iter);
+ if (err)
+ return err;
+ while (iov_iter_count(&iter)) {
+ unsigned long start, len;
+
+ start = untagged_addr((unsigned long)iter_iov_addr(&iter));
+ len = iter_iov_len(&iter);
+ err = do_mbind(start, len, &margs, flags);
+ if (err)
+ break;
+ iov_iter_advance(&iter, iter_iov_len(&iter));
+ }
+
+ kfree(iov);
+ return err;
+}
+
/* Set the process memory policy */
static long kernel_set_mempolicy(int mode, const unsigned long __user *nmask,
unsigned long maxnode)
--
2.39.1
^ permalink raw reply related
* [RFC PATCH 09/11] mm/mempolicy: add get_mempolicy2 syscall
From: Gregory Price @ 2023-12-07 0:27 UTC (permalink / raw)
To: linux-mm, jgroves, ravis.opensrc, sthanneeru, emirakhur,
Hasan.Maruf
Cc: linux-doc, linux-fsdevel, linux-api, linux-arch, linux-kernel,
akpm, arnd, tglx, luto, mingo, bp, dave.hansen, x86, hpa, mhocko,
tj, ying.huang, gregory.price, corbet, rakie.kim, hyeongtak.ji,
honggyu.kim, vtavarespetr, peterz, Michal Hocko
In-Reply-To: <20231207002759.51418-1-gregory.price@memverge.com>
get_mempolicy2 is an extensible get_mempolicy interface which allows
a user to retrieve the memory policy for a task or address.
Defined as:
get_mempolicy2(struct mpol_args *args, size_t size, unsigned long flags)
Input values include the following fields of mpol_args:
pol_nodes: if set, the nodemask of the policy returned here
pol_maxnodes: if pol_nodes is set, must describe max number of nodes
to be copied to pol_nodes
addr: if MPOL_F_ADDR is passed in `flags`, this address will be
used to return the mempolicy details of the vma the
address belongs to
flags: if MPOL_F_MEMS_ALLOWED, returns mems_allowed in pol_nodes
if MPOL_F_ADDR, return mempolicy info vma containing addr
else, returns per-task mempolicy information
Output values include the following fields of mpol_args:
mode: mempolicy mode
mode_flags: mempolicy mode flags
pol_nodes: if set, the nodemask for the mempolicy
policy_node: if the policy has extended node information, it will
be placed here. For example MPOL_INTERLEAVE will
return the next node which will be used for allocation
addr_node: If MPOL_F_ADDR is set, the numa node that the address
is located on will be returned.
home_node: policy home node will be returned here, or -1 if not.
MPOL_F_NODE has been dropped from get_mempolicy2 (it is ignored) in
favor or returning explicit values in `policy_node` and `addr_node`.
Suggested-by: Michal Hocko <mhocko@suse.com>
Signed-off-by: Gregory Price <gregory.price@memverge.com>
---
.../admin-guide/mm/numa_memory_policy.rst | 8 +++-
arch/alpha/kernel/syscalls/syscall.tbl | 1 +
arch/arm/tools/syscall.tbl | 1 +
arch/m68k/kernel/syscalls/syscall.tbl | 1 +
arch/microblaze/kernel/syscalls/syscall.tbl | 1 +
arch/mips/kernel/syscalls/syscall_n32.tbl | 1 +
arch/mips/kernel/syscalls/syscall_o32.tbl | 1 +
arch/parisc/kernel/syscalls/syscall.tbl | 1 +
arch/powerpc/kernel/syscalls/syscall.tbl | 1 +
arch/s390/kernel/syscalls/syscall.tbl | 1 +
arch/sh/kernel/syscalls/syscall.tbl | 1 +
arch/sparc/kernel/syscalls/syscall.tbl | 1 +
arch/x86/entry/syscalls/syscall_32.tbl | 1 +
arch/x86/entry/syscalls/syscall_64.tbl | 1 +
arch/xtensa/kernel/syscalls/syscall.tbl | 1 +
include/linux/syscalls.h | 2 +
include/uapi/asm-generic/unistd.h | 4 +-
mm/mempolicy.c | 43 +++++++++++++++++++
18 files changed, 69 insertions(+), 2 deletions(-)
diff --git a/Documentation/admin-guide/mm/numa_memory_policy.rst b/Documentation/admin-guide/mm/numa_memory_policy.rst
index 7195edaeaad9..82cdb765dd58 100644
--- a/Documentation/admin-guide/mm/numa_memory_policy.rst
+++ b/Documentation/admin-guide/mm/numa_memory_policy.rst
@@ -462,11 +462,17 @@ Get [Task] Memory Policy or Related Information::
long get_mempolicy(int *mode,
const unsigned long *nmask, unsigned long maxnode,
void *addr, int flags);
+ long get_mempolicy2(struct mpol_args args, size_t size,
+ unsigned long flags);
Queries the "task/process memory policy" of the calling task, or the
policy or location of a specified virtual address, depending on the
'flags' argument.
+get_mempolicy2() is an extended version of get_mempolicy() capable of
+acquiring extended information about a mempolicy, including those
+that can only be set via set_mempolicy2() or mbind2()..
+
See the get_mempolicy(2) man page for more details
@@ -523,7 +529,7 @@ Extended Mempolicy Arguments::
The extended mempolicy argument structure is defined to allow the mempolicy
interfaces future extensibility without the need for additional system calls.
-Extended interfaces (set_mempolicy2) use this argument structure.
+Extended interfaces (set_mempolicy2 and get_mempolicy2) use this structure.
The core arguments (mode, mode_flags, pol_nodes, and pol_maxnodes) apply to
all interfaces relative to their non-extended counterparts. Each additional
diff --git a/arch/alpha/kernel/syscalls/syscall.tbl b/arch/alpha/kernel/syscalls/syscall.tbl
index 0dc288a1118a..0301a8b0a262 100644
--- a/arch/alpha/kernel/syscalls/syscall.tbl
+++ b/arch/alpha/kernel/syscalls/syscall.tbl
@@ -497,3 +497,4 @@
565 common futex_wait sys_futex_wait
566 common futex_requeue sys_futex_requeue
567 common set_mempolicy2 sys_set_mempolicy2
+568 common get_mempolicy2 sys_get_mempolicy2
diff --git a/arch/arm/tools/syscall.tbl b/arch/arm/tools/syscall.tbl
index 50172ec0e1f5..771a33446e8e 100644
--- a/arch/arm/tools/syscall.tbl
+++ b/arch/arm/tools/syscall.tbl
@@ -471,3 +471,4 @@
455 common futex_wait sys_futex_wait
456 common futex_requeue sys_futex_requeue
457 common set_mempolicy2 sys_set_mempolicy2
+458 common get_mempolicy2 sys_get_mempolicy2
diff --git a/arch/m68k/kernel/syscalls/syscall.tbl b/arch/m68k/kernel/syscalls/syscall.tbl
index 839d90c535f2..048a409e684c 100644
--- a/arch/m68k/kernel/syscalls/syscall.tbl
+++ b/arch/m68k/kernel/syscalls/syscall.tbl
@@ -457,3 +457,4 @@
455 common futex_wait sys_futex_wait
456 common futex_requeue sys_futex_requeue
457 common set_mempolicy2 sys_set_mempolicy2
+458 common get_mempolicy2 sys_get_mempolicy2
diff --git a/arch/microblaze/kernel/syscalls/syscall.tbl b/arch/microblaze/kernel/syscalls/syscall.tbl
index 567c8b883735..327b01bd6793 100644
--- a/arch/microblaze/kernel/syscalls/syscall.tbl
+++ b/arch/microblaze/kernel/syscalls/syscall.tbl
@@ -463,3 +463,4 @@
455 common futex_wait sys_futex_wait
456 common futex_requeue sys_futex_requeue
457 common set_mempolicy2 sys_set_mempolicy2
+458 common get_mempolicy2 sys_get_mempolicy2
diff --git a/arch/mips/kernel/syscalls/syscall_n32.tbl b/arch/mips/kernel/syscalls/syscall_n32.tbl
index cc0640e16f2f..921d58e1da23 100644
--- a/arch/mips/kernel/syscalls/syscall_n32.tbl
+++ b/arch/mips/kernel/syscalls/syscall_n32.tbl
@@ -396,3 +396,4 @@
455 n32 futex_wait sys_futex_wait
456 n32 futex_requeue sys_futex_requeue
457 n32 set_mempolicy2 sys_set_mempolicy2
+458 n32 get_mempolicy2 sys_get_mempolicy2
diff --git a/arch/mips/kernel/syscalls/syscall_o32.tbl b/arch/mips/kernel/syscalls/syscall_o32.tbl
index f7262fde98d9..9271c83c9993 100644
--- a/arch/mips/kernel/syscalls/syscall_o32.tbl
+++ b/arch/mips/kernel/syscalls/syscall_o32.tbl
@@ -445,3 +445,4 @@
455 o32 futex_wait sys_futex_wait
456 o32 futex_requeue sys_futex_requeue
457 o32 set_mempolicy2 sys_set_mempolicy2
+458 o32 get_mempolicy2 sys_get_mempolicy2
diff --git a/arch/parisc/kernel/syscalls/syscall.tbl b/arch/parisc/kernel/syscalls/syscall.tbl
index e10f0e8bd064..0654f3f89fc7 100644
--- a/arch/parisc/kernel/syscalls/syscall.tbl
+++ b/arch/parisc/kernel/syscalls/syscall.tbl
@@ -456,3 +456,4 @@
455 common futex_wait sys_futex_wait
456 common futex_requeue sys_futex_requeue
457 common set_mempolicy2 sys_set_mempolicy2
+458 common get_mempolicy2 sys_get_mempolicy2
diff --git a/arch/powerpc/kernel/syscalls/syscall.tbl b/arch/powerpc/kernel/syscalls/syscall.tbl
index 4f03f5f42b78..ac11d2064e7a 100644
--- a/arch/powerpc/kernel/syscalls/syscall.tbl
+++ b/arch/powerpc/kernel/syscalls/syscall.tbl
@@ -544,3 +544,4 @@
455 common futex_wait sys_futex_wait
456 common futex_requeue sys_futex_requeue
457 common set_mempolicy2 sys_set_mempolicy2
+458 common get_mempolicy2 sys_get_mempolicy2
diff --git a/arch/s390/kernel/syscalls/syscall.tbl b/arch/s390/kernel/syscalls/syscall.tbl
index f98dadc2e9df..1cdcafe1ccca 100644
--- a/arch/s390/kernel/syscalls/syscall.tbl
+++ b/arch/s390/kernel/syscalls/syscall.tbl
@@ -460,3 +460,4 @@
455 common futex_wait sys_futex_wait sys_futex_wait
456 common futex_requeue sys_futex_requeue sys_futex_requeue
457 common set_mempolicy2 sys_set_mempolicy2 sys_set_mempolicy2
+458 common get_mempolicy2 sys_get_mempolicy2 sys_get_mempolicy2
diff --git a/arch/sh/kernel/syscalls/syscall.tbl b/arch/sh/kernel/syscalls/syscall.tbl
index f47ba9f2d05d..f71742024c29 100644
--- a/arch/sh/kernel/syscalls/syscall.tbl
+++ b/arch/sh/kernel/syscalls/syscall.tbl
@@ -460,3 +460,4 @@
455 common futex_wait sys_futex_wait
456 common futex_requeue sys_futex_requeue
457 common set_mempolicy2 sys_set_mempolicy2
+458 common get_mempolicy2 sys_get_mempolicy2
diff --git a/arch/sparc/kernel/syscalls/syscall.tbl b/arch/sparc/kernel/syscalls/syscall.tbl
index 53fb16616728..2fbf5dbe0620 100644
--- a/arch/sparc/kernel/syscalls/syscall.tbl
+++ b/arch/sparc/kernel/syscalls/syscall.tbl
@@ -503,3 +503,4 @@
455 common futex_wait sys_futex_wait
456 common futex_requeue sys_futex_requeue
457 common set_mempolicy2 sys_set_mempolicy2
+458 common get_mempolicy2 sys_get_mempolicy2
diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
index 4b4dc41b24ee..0af813b9a118 100644
--- a/arch/x86/entry/syscalls/syscall_32.tbl
+++ b/arch/x86/entry/syscalls/syscall_32.tbl
@@ -462,3 +462,4 @@
455 i386 futex_wait sys_futex_wait
456 i386 futex_requeue sys_futex_requeue
457 i386 set_mempolicy2 sys_set_mempolicy2
+458 i386 get_mempolicy2 sys_get_mempolicy2
diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
index 1bc2190bec27..0b777876fc15 100644
--- a/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/arch/x86/entry/syscalls/syscall_64.tbl
@@ -379,6 +379,7 @@
455 common futex_wait sys_futex_wait
456 common futex_requeue sys_futex_requeue
457 common set_mempolicy2 sys_set_mempolicy2
+458 common get_mempolicy2 sys_get_mempolicy2
#
# Due to a historical design error, certain syscalls are numbered differently
diff --git a/arch/xtensa/kernel/syscalls/syscall.tbl b/arch/xtensa/kernel/syscalls/syscall.tbl
index e26dc89399eb..4536c9a4227d 100644
--- a/arch/xtensa/kernel/syscalls/syscall.tbl
+++ b/arch/xtensa/kernel/syscalls/syscall.tbl
@@ -428,3 +428,4 @@
455 common futex_wait sys_futex_wait
456 common futex_requeue sys_futex_requeue
457 common set_mempolicy2 sys_set_mempolicy2
+458 common get_mempolicy2 sys_get_mempolicy2
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 3244cd990858..774512b7934e 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -820,6 +820,8 @@ asmlinkage long sys_get_mempolicy(int __user *policy,
unsigned long __user *nmask,
unsigned long maxnode,
unsigned long addr, unsigned long flags);
+asmlinkage long sys_get_mempolicy2(struct mpol_args *args, size_t size,
+ unsigned long flags);
asmlinkage long sys_set_mempolicy(int mode, const unsigned long __user *nmask,
unsigned long maxnode);
asmlinkage long sys_set_mempolicy2(struct mpol_args *args, size_t size,
diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h
index 55486aba099f..719accc731db 100644
--- a/include/uapi/asm-generic/unistd.h
+++ b/include/uapi/asm-generic/unistd.h
@@ -830,9 +830,11 @@ __SYSCALL(__NR_futex_wait, sys_futex_wait)
__SYSCALL(__NR_futex_requeue, sys_futex_requeue)
#define __NR_set_mempolicy2 457
__SYSCALL(__NR_set_mempolicy2, sys_set_mempolicy2)
+#define __NR_get_mempolicy2 458
+__SYSCALL(__NR_get_mempolicy2, sys_get_mempolicy2)
#undef __NR_syscalls
-#define __NR_syscalls 458
+#define __NR_syscalls 459
/*
* 32 bit systems traditionally used different
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index fdc56798226b..d1d10b2746e3 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -1860,6 +1860,49 @@ SYSCALL_DEFINE5(get_mempolicy, int __user *, policy,
return kernel_get_mempolicy(policy, nmask, maxnode, addr, flags);
}
+SYSCALL_DEFINE3(get_mempolicy2, struct mpol_args __user *, uargs, size_t, usize,
+ unsigned long, flags)
+{
+ struct mpol_args kargs;
+ struct mempolicy_args margs;
+ int err;
+ nodemask_t policy_nodemask;
+
+ err = copy_struct_from_user(&kargs, sizeof(kargs), uargs, usize);
+ if (err)
+ return -EINVAL;
+
+ if (flags & MPOL_F_MEMS_ALLOWED) {
+ if (!margs.policy_nodes)
+ return -EINVAL;
+ err = do_get_mems_allowed(&policy_nodemask);
+ if (err)
+ return err;
+ return copy_nodes_to_user(kargs.pol_nodes, kargs.pol_maxnodes,
+ &policy_nodemask);
+ }
+
+ margs.policy_nodes = kargs.pol_nodes ? &policy_nodemask : NULL;
+ if (flags & MPOL_F_ADDR) {
+ margs.addr = kargs.addr;
+ err = do_get_vma_mempolicy(&margs);
+ } else
+ err = do_get_task_mempolicy(&margs);
+
+ if (err)
+ return err;
+
+ kargs.mode = margs.mode;
+ kargs.mode_flags = margs.mode_flags;
+ kargs.policy_node = margs.policy_node;
+ kargs.addr_node = (flags & MPOL_F_ADDR) ? margs.addr_node : -1;
+ if (kargs.pol_nodes)
+ err = copy_nodes_to_user(kargs.pol_nodes, kargs.pol_maxnodes,
+ margs.policy_nodes);
+
+ return copy_to_user(uargs, &kargs, usize) ? -EFAULT : 0;
+}
+
bool vma_migratable(struct vm_area_struct *vma)
{
if (vma->vm_flags & (VM_IO | VM_PFNMAP))
--
2.39.1
^ permalink raw reply related
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