Compare commits
12 commits
9303543b61
...
ea54aeca4e
Author | SHA1 | Date | |
---|---|---|---|
ea54aeca4e | |||
d4e30e12ce | |||
853a6e07c9 | |||
0db646a43c | |||
7fa50ce178 | |||
9f5b50f6da | |||
ac58fa64a6 | |||
da28169adf | |||
81d6159d8c | |||
4c04a1f543 | |||
b8ed02e5b6 | |||
9372a4e417 |
6 changed files with 1171 additions and 49 deletions
84
README.md
84
README.md
|
@ -1,52 +1,68 @@
|
|||
### build_kernel
|
||||
This playbook is used to automatically build a kernel with an ACS overrides patch applied. Useful to isolate PCI-e devices to their own IOMMU group for VFIO passthrough.
|
||||
### build-kernel
|
||||
This playbook is used to automatically build a kernel with an ACS overrides patch applied as well as Valve's fsync patches.
|
||||
|
||||
See this page for kernel configuration details: https://copr.fedorainfracloud.org/coprs/jlay/kernel-acspatch/
|
||||
The ACS patch is useful to isolate PCI-e devices to their own IOMMU group for VFIO passthrough.
|
||||
|
||||
### Example usage
|
||||
The fsync patches improve Proton performance.
|
||||
|
||||
See this page for ACS kernel configuration details: https://copr.fedorainfracloud.org/coprs/jlay/kernel-acspatch/
|
||||
|
||||
### Requirements
|
||||
- Inventory with one host
|
||||
- `hosts-local` provided as a sample inventory for localhost
|
||||
- This host must be running Fedora (builds use mock)
|
||||
|
||||
### Usage
|
||||
|
||||
#### Notes
|
||||
Target Fedora version is controlled by the variable `fedora_version` in [./playbook.yml](./playbook.yml)
|
||||
|
||||
Patch to apply is determined by the `patch_file` variable, stored in [./payload/](./payload)
|
||||
|
||||
Patches for many different kernel versions provided, the highest version available should be current/working. Patch versions only change when `diffs` change.
|
||||
|
||||
#### Output:
|
||||
```
|
||||
jlay@workstation:~/git/ansible/build_kernel$ ansible-playbook -i hosts playbook.yml
|
||||
$ ansible-playbook -i buildserver playbook.yml -b --ask-become-pass
|
||||
BECOME password:
|
||||
|
||||
PLAY [buildservers] ******************************************************************************************************************************************
|
||||
PLAY [buildservers] *******************************************************************************************************************************************
|
||||
|
||||
TASK [Gathering Facts] ******************************************************************************************************************************************
|
||||
ok: [delta.jlay.io]
|
||||
TASK [Gathering Facts] ****************************************************************************************************************************************
|
||||
ok: [buildserver]
|
||||
|
||||
TASK [Ensure libselinux-python is installed] ******************************************************************************************************************************************
|
||||
ok: [delta.jlay.io]
|
||||
TASK [Ensure fedpkg is installed] *****************************************************************************************************************************
|
||||
changed: [buildserver]
|
||||
|
||||
TASK [Ensure fedpkg is installed] ******************************************************************************************************************************************
|
||||
ok: [delta.jlay.io]
|
||||
TASK [Find old mock chroots] **********************************************************************************************************************************
|
||||
ok: [buildserver]
|
||||
|
||||
TASK [Find old mock chroots] ******************************************************************************************************************************************
|
||||
ok: [delta.jlay.io]
|
||||
TASK [Clean old mock chroots] *********************************************************************************************************************************
|
||||
|
||||
TASK [Clean old mock chroots] ******************************************************************************************************************************************
|
||||
TASK [Clean old build dir] ************************************************************************************************************************************
|
||||
ok: [buildserver]
|
||||
|
||||
TASK [Clean old build dir] ******************************************************************************************************************************************
|
||||
changed: [delta.jlay.io]
|
||||
TASK [Clone f32 kernel to /tmp/f32_kernel] ********************************************************************************************************************
|
||||
changed: [buildserver]
|
||||
|
||||
TASK [Clone f29 kernel to /tmp/f29_kernel] ******************************************************************************************************************************************
|
||||
changed: [delta.jlay.io]
|
||||
TASK [Copy patch to build dir] ********************************************************************************************************************************
|
||||
changed: [buildserver]
|
||||
|
||||
TASK [Copy patch to build dir] ******************************************************************************************************************************************
|
||||
changed: [delta.jlay.io]
|
||||
TASK [Copy newpatch.sh on master branch, for 5.6 on f31] ******************************************************************************************************
|
||||
changed: [buildserver]
|
||||
|
||||
TASK [Apply patch and build kernel (logs in /tmp/f29_kernel/results_kernel/*)] ******************************************************************************************************************************************
|
||||
changed: [delta.jlay.io] => (item=./scripts/newpatch.sh acso-4.18.patch)
|
||||
changed: [delta.jlay.io] => (item=/usr/bin/sed -i -e 's/%define buildid .*$/%define buildid .acspatch/' kernel.spec)
|
||||
changed: [delta.jlay.io] => (item=/usr/bin/make release)
|
||||
changed: [delta.jlay.io] => (item=fedpkg mockbuild)
|
||||
TASK [Apply patch and build kernel (logs in /tmp/f32_kernel/results_kernel/*)] ********************************************************************************
|
||||
changed: [buildserver] => (item=./scripts/newpatch.sh acso-5.4.6.patch)
|
||||
changed: [buildserver] => (item=/usr/bin/sed -i -e 's/%define buildid .*$/%define buildid .acspatch/' kernel.spec)
|
||||
changed: [buildserver] => (item=/usr/bin/make release)
|
||||
changed: [buildserver] => (item=fedpkg mockbuild)
|
||||
|
||||
TASK [Find RPMs in /tmp/f29_kernel/results_kernel/*/*/*.rpm] ******************************************************************************************************************************************
|
||||
ok: [delta.jlay.io]
|
||||
TASK [Find RPMs in /tmp/f32_kernel/results_kernel/*/*/*.rpm] **************************************************************************************************
|
||||
ok: [buildserver]
|
||||
|
||||
TASK [Copy RPMs to /tmp/ (local)] ******************************************************************************************************************************************
|
||||
ok: [delta.jlay.io] => (item={'uid': 0, 'woth': False, 'mtime': 1545900251.552329, 'inode': 1086935, 'isgid': False, 'size': 104419185, 'roth': True, 'isuid': False, 'isreg': True, 'pw_name': 'root', 'gid': 135, 'ischr': False, 'wusr': True, 'xoth': False, 'rusr': True, 'nlink': 1, 'issock': False, 'rgrp': True, 'gr_name': 'mock', 'path': '/tmp/f29_kernel/results_kernel/4.19.12/302.acspatch.fc29/kernel-4.19.12-302.acspatch.fc29.src.rpm', 'xusr': False, 'atime': 1545932715.7957838, 'isdir': False, 'ctime': 1545926304.4206192, 'isblk': False, 'xgrp': False, 'dev': 41, 'wgrp': False, 'isfifo': False, 'mode': '0644', 'islnk': False})
|
||||
TASK [Copy RPMs to /tmp/ (local)] *****************************************************************************************************************************
|
||||
[...]
|
||||
|
||||
PLAY RECAP ******************************************************************************************************************************************
|
||||
delta.jlay.io : ok=8 changed=4 unreachable=0 failed=0
|
||||
|
||||
jlay@workstation:~/git/ansible/build_kernel$
|
||||
PLAY RECAP ****************************************************************************************************************************************************
|
||||
buildserver : ok=10 changed=6 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
|
||||
```
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[defaults]
|
||||
nocows = True
|
||||
command_warnings = False
|
||||
retry_files_enabled = False
|
||||
#strategy_plugins = ./mitogen-0.2.7/ansible_mitogen/plugins/strategy
|
||||
|
|
|
@ -3,3 +3,4 @@ localhost
|
|||
|
||||
[buildservers:vars]
|
||||
ansible_connection=local
|
||||
ansible_python_interpreter=/usr/bin/python3
|
||||
|
|
187
payload/acso-5.4.6.patch
Normal file
187
payload/acso-5.4.6.patch
Normal file
|
@ -0,0 +1,187 @@
|
|||
From: Josh Lay <me@jlay.io>
|
||||
Date: Fri, 12 Oct 2018 11:17:23 -0600
|
||||
Subject: [PATCH] pci: Enable overrides for missing ACS capabilities (4.18)
|
||||
|
||||
This an updated version of Alex Williamson's patch from:
|
||||
https://lkml.org/lkml/2013/5/30/513
|
||||
|
||||
To build on Fedora 28 kernels 4.18.x
|
||||
|
||||
Original commit message follows:
|
||||
---
|
||||
PCIe ACS (Access Control Services) is the PCIe 2.0+ feature that
|
||||
allows us to control whether transactions are allowed to be redirected
|
||||
in various subnodes of a PCIe topology. For instance, if two
|
||||
endpoints are below a root port or downsteam switch port, the
|
||||
downstream port may optionally redirect transactions between the
|
||||
devices, bypassing upstream devices. The same can happen internally
|
||||
on multifunction devices. The transaction may never be visible to the
|
||||
upstream devices.
|
||||
|
||||
One upstream device that we particularly care about is the IOMMU. If
|
||||
a redirection occurs in the topology below the IOMMU, then the IOMMU
|
||||
cannot provide isolation between devices. This is why the PCIe spec
|
||||
encourages topologies to include ACS support. Without it, we have to
|
||||
assume peer-to-peer DMA within a hierarchy can bypass IOMMU isolation.
|
||||
|
||||
Unfortunately, far too many topologies do not support ACS to make this
|
||||
a steadfast requirement. Even the latest chipsets from Intel are only
|
||||
sporadically supporting ACS. We have trouble getting interconnect
|
||||
vendors to include the PCIe spec required PCIe capability, let alone
|
||||
suggested features.
|
||||
|
||||
Therefore, we need to add some flexibility. The pcie_acs_override=
|
||||
boot option lets users opt-in specific devices or sets of devices to
|
||||
assume ACS support. The "downstream" option assumes full ACS support
|
||||
on root ports and downstream switch ports. The "multifunction"
|
||||
option assumes the subset of ACS features available on multifunction
|
||||
endpoints and upstream switch ports are supported. The "id:nnnn:nnnn"
|
||||
option enables ACS support on devices matching the provided vendor
|
||||
and device IDs, allowing more strategic ACS overrides. These options
|
||||
may be combined in any order. A maximum of 16 id specific overrides
|
||||
are available. It's suggested to use the most limited set of options
|
||||
necessary to avoid completely disabling ACS across the topology.
|
||||
Note to hardware vendors, we have facilities to permanently quirk
|
||||
specific devices which enforce isolation but not provide an ACS
|
||||
capability. Please contact me to have your devices added and save
|
||||
your customers the hassle of this boot option.
|
||||
|
||||
Signed-off-by: Mark Weiman <mark.weiman@markzz.com>
|
||||
---
|
||||
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
|
||||
index efc7aa7a0670..b5258db83441 100644
|
||||
--- a/Documentation/admin-guide/kernel-parameters.txt
|
||||
+++ b/Documentation/admin-guide/kernel-parameters.txt
|
||||
@@ -3028,6 +3028,15 @@
|
||||
nomsi [MSI] If the PCI_MSI kernel config parameter is
|
||||
enabled, this kernel boot option can be used to
|
||||
disable the use of MSI interrupts system-wide.
|
||||
+ pcie_acs_override =
|
||||
+ [PCIE] Override missing PCIe ACS support for:
|
||||
+ downstream
|
||||
+ All downstream ports - full ACS capabilities
|
||||
+ multfunction
|
||||
+ All multifunction devices - multifunction ACS subset
|
||||
+ id:nnnn:nnnn
|
||||
+ Specfic device - full ACS capabilities
|
||||
+ Specified as vid:did (vendor/device ID) in hex
|
||||
noioapicquirk [APIC] Disable all boot interrupt quirks.
|
||||
Safety option to keep boot IRQs enabled. This
|
||||
should never be necessary.
|
||||
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
|
||||
index f439de848658..6431f60a56f4 100644
|
||||
--- a/drivers/pci/quirks.c
|
||||
+++ b/drivers/pci/quirks.c
|
||||
@@ -3481,6 +3481,106 @@ static void quirk_no_bus_reset(struct pci_dev *dev)
|
||||
dev->dev_flags |= PCI_DEV_FLAGS_NO_BUS_RESET;
|
||||
}
|
||||
|
||||
+static bool acs_on_downstream;
|
||||
+static bool acs_on_multifunction;
|
||||
+
|
||||
+#define NUM_ACS_IDS 16
|
||||
+struct acs_on_id {
|
||||
+ unsigned short vendor;
|
||||
+ unsigned short device;
|
||||
+};
|
||||
+static struct acs_on_id acs_on_ids[NUM_ACS_IDS];
|
||||
+static u8 max_acs_id;
|
||||
+
|
||||
+static __init int pcie_acs_override_setup(char *p)
|
||||
+{
|
||||
+ if (!p)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ while (*p) {
|
||||
+ if (!strncmp(p, "downstream", 10))
|
||||
+ acs_on_downstream = true;
|
||||
+ if (!strncmp(p, "multifunction", 13))
|
||||
+ acs_on_multifunction = true;
|
||||
+ if (!strncmp(p, "id:", 3)) {
|
||||
+ char opt[5];
|
||||
+ int ret;
|
||||
+ long val;
|
||||
+
|
||||
+ if (max_acs_id >= NUM_ACS_IDS - 1) {
|
||||
+ pr_warn("Out of PCIe ACS override slots (%d)\n",
|
||||
+ NUM_ACS_IDS);
|
||||
+ goto next;
|
||||
+ }
|
||||
+
|
||||
+ p += 3;
|
||||
+ snprintf(opt, 5, "%s", p);
|
||||
+ ret = kstrtol(opt, 16, &val);
|
||||
+ if (ret) {
|
||||
+ pr_warn("PCIe ACS ID parse error %d\n", ret);
|
||||
+ goto next;
|
||||
+ }
|
||||
+ acs_on_ids[max_acs_id].vendor = val;
|
||||
+
|
||||
+ p += strcspn(p, ":");
|
||||
+ if (*p != ':') {
|
||||
+ pr_warn("PCIe ACS invalid ID\n");
|
||||
+ goto next;
|
||||
+ }
|
||||
+
|
||||
+ p++;
|
||||
+ snprintf(opt, 5, "%s", p);
|
||||
+ ret = kstrtol(opt, 16, &val);
|
||||
+ if (ret) {
|
||||
+ pr_warn("PCIe ACS ID parse error %d\n", ret);
|
||||
+ goto next;
|
||||
+ }
|
||||
+ acs_on_ids[max_acs_id].device = val;
|
||||
+ max_acs_id++;
|
||||
+ }
|
||||
+next:
|
||||
+ p += strcspn(p, ",");
|
||||
+ if (*p == ',')
|
||||
+ p++;
|
||||
+ }
|
||||
+
|
||||
+ if (acs_on_downstream || acs_on_multifunction || max_acs_id)
|
||||
+ pr_warn("Warning: PCIe ACS overrides enabled; This may allow non-IOMMU protected peer-to-peer DMA\n");
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+early_param("pcie_acs_override", pcie_acs_override_setup);
|
||||
+
|
||||
+static int pcie_acs_overrides(struct pci_dev *dev, u16 acs_flags)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ /* Never override ACS for legacy devices or devices with ACS caps */
|
||||
+ if (!pci_is_pcie(dev) ||
|
||||
+ pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS))
|
||||
+ return -ENOTTY;
|
||||
+
|
||||
+ for (i = 0; i < max_acs_id; i++)
|
||||
+ if (acs_on_ids[i].vendor == dev->vendor &&
|
||||
+ acs_on_ids[i].device == dev->device)
|
||||
+ return 1;
|
||||
+
|
||||
+ switch (pci_pcie_type(dev)) {
|
||||
+ case PCI_EXP_TYPE_DOWNSTREAM:
|
||||
+ case PCI_EXP_TYPE_ROOT_PORT:
|
||||
+ if (acs_on_downstream)
|
||||
+ return 1;
|
||||
+ break;
|
||||
+ case PCI_EXP_TYPE_ENDPOINT:
|
||||
+ case PCI_EXP_TYPE_UPSTREAM:
|
||||
+ case PCI_EXP_TYPE_LEG_END:
|
||||
+ case PCI_EXP_TYPE_RC_END:
|
||||
+ if (acs_on_multifunction && dev->multifunction)
|
||||
+ return 1;
|
||||
+ }
|
||||
+
|
||||
+ return -ENOTTY;
|
||||
+}
|
||||
/*
|
||||
* Some Atheros AR9xxx and QCA988x chips do not behave after a bus reset.
|
||||
* The device will throw a Link Down error on AER-capable systems and
|
||||
@@ -4660,4 +4760,5 @@
|
||||
{ PCI_VENDOR_ID_AMPERE, 0xE00A, pci_quirk_xgene_acs },
|
||||
{ PCI_VENDOR_ID_AMPERE, 0xE00B, pci_quirk_xgene_acs },
|
||||
{ PCI_VENDOR_ID_AMPERE, 0xE00C, pci_quirk_xgene_acs },
|
||||
+ { PCI_ANY_ID, PCI_ANY_ID, pcie_acs_overrides },
|
||||
{ PCI_VENDOR_ID_BROADCOM, 0xD714, pci_quirk_brcm_acs },
|
909
payload/fsync-5.6.patch
Normal file
909
payload/fsync-5.6.patch
Normal file
|
@ -0,0 +1,909 @@
|
|||
From f7f49141a5dbe9c99d78196b58c44307fb2e6be3 Mon Sep 17 00:00:00 2001
|
||||
From: Tk-Glitch <ti3nou@gmail.com>
|
||||
Date: Mon, 20 Apr 2020 14:09:11 +0200
|
||||
Subject: Import Fsync v3 patchset - Squashed from https://gitlab.collabora.com/tonyk/linux/-/commits/futex-proton-v3
|
||||
|
||||
diff --git a/include/uapi/linux/futex.h b/include/uapi/linux/futex.h
|
||||
index a89eb0accd5e2ee527be1e3e11b1117ff5bf94b4..580001e89c6caed57dd8b3cb491d65dce846caff 100644
|
||||
--- a/include/uapi/linux/futex.h
|
||||
+++ b/include/uapi/linux/futex.h
|
||||
@@ -21,6 +21,7 @@
|
||||
#define FUTEX_WAKE_BITSET 10
|
||||
#define FUTEX_WAIT_REQUEUE_PI 11
|
||||
#define FUTEX_CMP_REQUEUE_PI 12
|
||||
+#define FUTEX_WAIT_MULTIPLE 13
|
||||
|
||||
#define FUTEX_PRIVATE_FLAG 128
|
||||
#define FUTEX_CLOCK_REALTIME 256
|
||||
@@ -40,6 +41,8 @@
|
||||
FUTEX_PRIVATE_FLAG)
|
||||
#define FUTEX_CMP_REQUEUE_PI_PRIVATE (FUTEX_CMP_REQUEUE_PI | \
|
||||
FUTEX_PRIVATE_FLAG)
|
||||
+#define FUTEX_WAIT_MULTIPLE_PRIVATE (FUTEX_WAIT_MULTIPLE | \
|
||||
+ FUTEX_PRIVATE_FLAG)
|
||||
|
||||
/*
|
||||
* Support for robust futexes: the kernel cleans up held futexes at
|
||||
@@ -150,4 +153,21 @@ struct robust_list_head {
|
||||
(((op & 0xf) << 28) | ((cmp & 0xf) << 24) \
|
||||
| ((oparg & 0xfff) << 12) | (cmparg & 0xfff))
|
||||
|
||||
+/*
|
||||
+ * Maximum number of multiple futexes to wait for
|
||||
+ */
|
||||
+#define FUTEX_MULTIPLE_MAX_COUNT 128
|
||||
+
|
||||
+/**
|
||||
+ * struct futex_wait_block - Block of futexes to be waited for
|
||||
+ * @uaddr: User address of the futex
|
||||
+ * @val: Futex value expected by userspace
|
||||
+ * @bitset: Bitset for the optional bitmasked wakeup
|
||||
+ */
|
||||
+struct futex_wait_block {
|
||||
+ __u32 __user *uaddr;
|
||||
+ __u32 val;
|
||||
+ __u32 bitset;
|
||||
+};
|
||||
+
|
||||
#endif /* _UAPI_LINUX_FUTEX_H */
|
||||
diff --git a/kernel/futex.c b/kernel/futex.c
|
||||
index 0cf84c8664f207c574325b899ef2e57f01295a94..58cf9eb2b851b4858e29b5ef4114a29a92e676ba 100644
|
||||
--- a/kernel/futex.c
|
||||
+++ b/kernel/futex.c
|
||||
@@ -215,6 +215,8 @@ struct futex_pi_state {
|
||||
* @rt_waiter: rt_waiter storage for use with requeue_pi
|
||||
* @requeue_pi_key: the requeue_pi target futex key
|
||||
* @bitset: bitset for the optional bitmasked wakeup
|
||||
+ * @uaddr: userspace address of futex
|
||||
+ * @uval: expected futex's value
|
||||
*
|
||||
* We use this hashed waitqueue, instead of a normal wait_queue_entry_t, so
|
||||
* we can wake only the relevant ones (hashed queues may be shared).
|
||||
@@ -237,6 +239,8 @@ struct futex_q {
|
||||
struct rt_mutex_waiter *rt_waiter;
|
||||
union futex_key *requeue_pi_key;
|
||||
u32 bitset;
|
||||
+ u32 __user *uaddr;
|
||||
+ u32 uval;
|
||||
} __randomize_layout;
|
||||
|
||||
static const struct futex_q futex_q_init = {
|
||||
@@ -2420,6 +2424,29 @@ static int unqueue_me(struct futex_q *q)
|
||||
return ret;
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * unqueue_multiple() - Remove several futexes from their futex_hash_bucket
|
||||
+ * @q: The list of futexes to unqueue
|
||||
+ * @count: Number of futexes in the list
|
||||
+ *
|
||||
+ * Helper to unqueue a list of futexes. This can't fail.
|
||||
+ *
|
||||
+ * Return:
|
||||
+ * - >=0 - Index of the last futex that was awoken;
|
||||
+ * - -1 - If no futex was awoken
|
||||
+ */
|
||||
+static int unqueue_multiple(struct futex_q *q, int count)
|
||||
+{
|
||||
+ int ret = -1;
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < count; i++) {
|
||||
+ if (!unqueue_me(&q[i]))
|
||||
+ ret = i;
|
||||
+ }
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* PI futexes can not be requeued and must remove themself from the
|
||||
* hash bucket. The hash bucket lock (i.e. lock_ptr) is held on entry
|
||||
@@ -2783,6 +2810,211 @@ static int futex_wait_setup(u32 __user *uaddr, u32 val, unsigned int flags,
|
||||
return ret;
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * futex_wait_multiple_setup() - Prepare to wait and enqueue multiple futexes
|
||||
+ * @qs: The corresponding futex list
|
||||
+ * @count: The size of the lists
|
||||
+ * @flags: Futex flags (FLAGS_SHARED, etc.)
|
||||
+ * @awaken: Index of the last awoken futex
|
||||
+ *
|
||||
+ * Prepare multiple futexes in a single step and enqueue them. This may fail if
|
||||
+ * the futex list is invalid or if any futex was already awoken. On success the
|
||||
+ * task is ready to interruptible sleep.
|
||||
+ *
|
||||
+ * Return:
|
||||
+ * - 1 - One of the futexes was awaken by another thread
|
||||
+ * - 0 - Success
|
||||
+ * - <0 - -EFAULT, -EWOULDBLOCK or -EINVAL
|
||||
+ */
|
||||
+static int futex_wait_multiple_setup(struct futex_q *qs, int count,
|
||||
+ unsigned int flags, int *awaken)
|
||||
+{
|
||||
+ struct futex_hash_bucket *hb;
|
||||
+ int ret, i;
|
||||
+ u32 uval;
|
||||
+
|
||||
+ /*
|
||||
+ * Enqueuing multiple futexes is tricky, because we need to
|
||||
+ * enqueue each futex in the list before dealing with the next
|
||||
+ * one to avoid deadlocking on the hash bucket. But, before
|
||||
+ * enqueuing, we need to make sure that current->state is
|
||||
+ * TASK_INTERRUPTIBLE, so we don't absorb any awake events, which
|
||||
+ * cannot be done before the get_futex_key of the next key,
|
||||
+ * because it calls get_user_pages, which can sleep. Thus, we
|
||||
+ * fetch the list of futexes keys in two steps, by first pinning
|
||||
+ * all the memory keys in the futex key, and only then we read
|
||||
+ * each key and queue the corresponding futex.
|
||||
+ */
|
||||
+retry:
|
||||
+ for (i = 0; i < count; i++) {
|
||||
+ qs[i].key = FUTEX_KEY_INIT;
|
||||
+ ret = get_futex_key(qs[i].uaddr, flags & FLAGS_SHARED,
|
||||
+ &qs[i].key, FUTEX_READ);
|
||||
+ if (unlikely(ret)) {
|
||||
+ for (--i; i >= 0; i--)
|
||||
+ put_futex_key(&qs[i].key);
|
||||
+ return ret;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ set_current_state(TASK_INTERRUPTIBLE);
|
||||
+
|
||||
+ for (i = 0; i < count; i++) {
|
||||
+ struct futex_q *q = &qs[i];
|
||||
+
|
||||
+ hb = queue_lock(q);
|
||||
+
|
||||
+ ret = get_futex_value_locked(&uval, q->uaddr);
|
||||
+ if (ret) {
|
||||
+ /*
|
||||
+ * We need to try to handle the fault, which
|
||||
+ * cannot be done without sleep, so we need to
|
||||
+ * undo all the work already done, to make sure
|
||||
+ * we don't miss any wake ups. Therefore, clean
|
||||
+ * up, handle the fault and retry from the
|
||||
+ * beginning.
|
||||
+ */
|
||||
+ queue_unlock(hb);
|
||||
+
|
||||
+ /*
|
||||
+ * Keys 0..(i-1) are implicitly put
|
||||
+ * on unqueue_multiple.
|
||||
+ */
|
||||
+ put_futex_key(&q->key);
|
||||
+
|
||||
+ *awaken = unqueue_multiple(qs, i);
|
||||
+
|
||||
+ __set_current_state(TASK_RUNNING);
|
||||
+
|
||||
+ /*
|
||||
+ * On a real fault, prioritize the error even if
|
||||
+ * some other futex was awoken. Userspace gave
|
||||
+ * us a bad address, -EFAULT them.
|
||||
+ */
|
||||
+ ret = get_user(uval, q->uaddr);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /*
|
||||
+ * Even if the page fault was handled, If
|
||||
+ * something was already awaken, we can safely
|
||||
+ * give up and succeed to give a hint for userspace to
|
||||
+ * acquire the right futex faster.
|
||||
+ */
|
||||
+ if (*awaken >= 0)
|
||||
+ return 1;
|
||||
+
|
||||
+ goto retry;
|
||||
+ }
|
||||
+
|
||||
+ if (uval != q->uval) {
|
||||
+ queue_unlock(hb);
|
||||
+
|
||||
+ put_futex_key(&qs[i].key);
|
||||
+
|
||||
+ /*
|
||||
+ * If something was already awaken, we can
|
||||
+ * safely ignore the error and succeed.
|
||||
+ */
|
||||
+ *awaken = unqueue_multiple(qs, i);
|
||||
+ __set_current_state(TASK_RUNNING);
|
||||
+ if (*awaken >= 0)
|
||||
+ return 1;
|
||||
+
|
||||
+ return -EWOULDBLOCK;
|
||||
+ }
|
||||
+
|
||||
+ /*
|
||||
+ * The bucket lock can't be held while dealing with the
|
||||
+ * next futex. Queue each futex at this moment so hb can
|
||||
+ * be unlocked.
|
||||
+ */
|
||||
+ queue_me(&qs[i], hb);
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * futex_wait_multiple() - Prepare to wait on and enqueue several futexes
|
||||
+ * @qs: The list of futexes to wait on
|
||||
+ * @op: Operation code from futex's syscall
|
||||
+ * @count: The number of objects
|
||||
+ * @abs_time: Timeout before giving up and returning to userspace
|
||||
+ *
|
||||
+ * Entry point for the FUTEX_WAIT_MULTIPLE futex operation, this function
|
||||
+ * sleeps on a group of futexes and returns on the first futex that
|
||||
+ * triggered, or after the timeout has elapsed.
|
||||
+ *
|
||||
+ * Return:
|
||||
+ * - >=0 - Hint to the futex that was awoken
|
||||
+ * - <0 - On error
|
||||
+ */
|
||||
+static int futex_wait_multiple(struct futex_q *qs, int op,
|
||||
+ u32 count, ktime_t *abs_time)
|
||||
+{
|
||||
+ struct hrtimer_sleeper timeout, *to;
|
||||
+ int ret, flags = 0, hint = 0;
|
||||
+ unsigned int i;
|
||||
+
|
||||
+ if (!(op & FUTEX_PRIVATE_FLAG))
|
||||
+ flags |= FLAGS_SHARED;
|
||||
+
|
||||
+ if (op & FUTEX_CLOCK_REALTIME)
|
||||
+ flags |= FLAGS_CLOCKRT;
|
||||
+
|
||||
+ to = futex_setup_timer(abs_time, &timeout, flags, 0);
|
||||
+ while (1) {
|
||||
+ ret = futex_wait_multiple_setup(qs, count, flags, &hint);
|
||||
+ if (ret) {
|
||||
+ if (ret > 0) {
|
||||
+ /* A futex was awaken during setup */
|
||||
+ ret = hint;
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ if (to)
|
||||
+ hrtimer_start_expires(&to->timer, HRTIMER_MODE_ABS);
|
||||
+
|
||||
+ /*
|
||||
+ * Avoid sleeping if another thread already tried to
|
||||
+ * wake us.
|
||||
+ */
|
||||
+ for (i = 0; i < count; i++) {
|
||||
+ if (plist_node_empty(&qs[i].list))
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ if (i == count && (!to || to->task))
|
||||
+ freezable_schedule();
|
||||
+
|
||||
+ ret = unqueue_multiple(qs, count);
|
||||
+
|
||||
+ __set_current_state(TASK_RUNNING);
|
||||
+
|
||||
+ if (ret >= 0)
|
||||
+ break;
|
||||
+ if (to && !to->task) {
|
||||
+ ret = -ETIMEDOUT;
|
||||
+ break;
|
||||
+ } else if (signal_pending(current)) {
|
||||
+ ret = -ERESTARTSYS;
|
||||
+ break;
|
||||
+ }
|
||||
+ /*
|
||||
+ * The final case is a spurious wakeup, for
|
||||
+ * which just retry.
|
||||
+ */
|
||||
+ }
|
||||
+
|
||||
+ if (to) {
|
||||
+ hrtimer_cancel(&to->timer);
|
||||
+ destroy_hrtimer_on_stack(&to->timer);
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
static int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val,
|
||||
ktime_t *abs_time, u32 bitset)
|
||||
{
|
||||
@@ -3907,6 +4139,43 @@ long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * futex_read_wait_block - Read an array of futex_wait_block from userspace
|
||||
+ * @uaddr: Userspace address of the block
|
||||
+ * @count: Number of blocks to be read
|
||||
+ *
|
||||
+ * This function creates and allocate an array of futex_q (we zero it to
|
||||
+ * initialize the fields) and then, for each futex_wait_block element from
|
||||
+ * userspace, fill a futex_q element with proper values.
|
||||
+ */
|
||||
+inline struct futex_q *futex_read_wait_block(u32 __user *uaddr, u32 count)
|
||||
+{
|
||||
+ unsigned int i;
|
||||
+ struct futex_q *qs;
|
||||
+ struct futex_wait_block fwb;
|
||||
+ struct futex_wait_block __user *entry =
|
||||
+ (struct futex_wait_block __user *)uaddr;
|
||||
+
|
||||
+ if (!count || count > FUTEX_MULTIPLE_MAX_COUNT)
|
||||
+ return ERR_PTR(-EINVAL);
|
||||
+
|
||||
+ qs = kcalloc(count, sizeof(*qs), GFP_KERNEL);
|
||||
+ if (!qs)
|
||||
+ return ERR_PTR(-ENOMEM);
|
||||
+
|
||||
+ for (i = 0; i < count; i++) {
|
||||
+ if (copy_from_user(&fwb, &entry[i], sizeof(fwb))) {
|
||||
+ kfree(qs);
|
||||
+ return ERR_PTR(-EFAULT);
|
||||
+ }
|
||||
+
|
||||
+ qs[i].uaddr = fwb.uaddr;
|
||||
+ qs[i].uval = fwb.val;
|
||||
+ qs[i].bitset = fwb.bitset;
|
||||
+ }
|
||||
+
|
||||
+ return qs;
|
||||
+}
|
||||
|
||||
SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,
|
||||
struct __kernel_timespec __user *, utime, u32 __user *, uaddr2,
|
||||
@@ -3919,7 +4188,8 @@ SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,
|
||||
|
||||
if (utime && (cmd == FUTEX_WAIT || cmd == FUTEX_LOCK_PI ||
|
||||
cmd == FUTEX_WAIT_BITSET ||
|
||||
- cmd == FUTEX_WAIT_REQUEUE_PI)) {
|
||||
+ cmd == FUTEX_WAIT_REQUEUE_PI ||
|
||||
+ cmd == FUTEX_WAIT_MULTIPLE)) {
|
||||
if (unlikely(should_fail_futex(!(op & FUTEX_PRIVATE_FLAG))))
|
||||
return -EFAULT;
|
||||
if (get_timespec64(&ts, utime))
|
||||
@@ -3940,6 +4210,25 @@ SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,
|
||||
cmd == FUTEX_CMP_REQUEUE_PI || cmd == FUTEX_WAKE_OP)
|
||||
val2 = (u32) (unsigned long) utime;
|
||||
|
||||
+ if (cmd == FUTEX_WAIT_MULTIPLE) {
|
||||
+ int ret;
|
||||
+ struct futex_q *qs;
|
||||
+
|
||||
+#ifdef CONFIG_X86_X32
|
||||
+ if (unlikely(in_x32_syscall()))
|
||||
+ return -ENOSYS;
|
||||
+#endif
|
||||
+ qs = futex_read_wait_block(uaddr, val);
|
||||
+
|
||||
+ if (IS_ERR(qs))
|
||||
+ return PTR_ERR(qs);
|
||||
+
|
||||
+ ret = futex_wait_multiple(qs, op, val, tp);
|
||||
+ kfree(qs);
|
||||
+
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
return do_futex(uaddr, op, val, tp, uaddr2, val2, val3);
|
||||
}
|
||||
|
||||
@@ -4102,6 +4391,57 @@ COMPAT_SYSCALL_DEFINE3(get_robust_list, int, pid,
|
||||
#endif /* CONFIG_COMPAT */
|
||||
|
||||
#ifdef CONFIG_COMPAT_32BIT_TIME
|
||||
+/**
|
||||
+ * struct compat_futex_wait_block - Block of futexes to be waited for
|
||||
+ * @uaddr: User address of the futex (compatible pointer)
|
||||
+ * @val: Futex value expected by userspace
|
||||
+ * @bitset: Bitset for the optional bitmasked wakeup
|
||||
+ */
|
||||
+struct compat_futex_wait_block {
|
||||
+ compat_uptr_t uaddr;
|
||||
+ __u32 val;
|
||||
+ __u32 bitset;
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
+ * compat_futex_read_wait_block - Read an array of futex_wait_block from
|
||||
+ * userspace
|
||||
+ * @uaddr: Userspace address of the block
|
||||
+ * @count: Number of blocks to be read
|
||||
+ *
|
||||
+ * This function does the same as futex_read_wait_block(), except that it
|
||||
+ * converts the pointer to the futex from the compat version to the regular one.
|
||||
+ */
|
||||
+inline struct futex_q *compat_futex_read_wait_block(u32 __user *uaddr,
|
||||
+ u32 count)
|
||||
+{
|
||||
+ unsigned int i;
|
||||
+ struct futex_q *qs;
|
||||
+ struct compat_futex_wait_block fwb;
|
||||
+ struct compat_futex_wait_block __user *entry =
|
||||
+ (struct compat_futex_wait_block __user *)uaddr;
|
||||
+
|
||||
+ if (!count || count > FUTEX_MULTIPLE_MAX_COUNT)
|
||||
+ return ERR_PTR(-EINVAL);
|
||||
+
|
||||
+ qs = kcalloc(count, sizeof(*qs), GFP_KERNEL);
|
||||
+ if (!qs)
|
||||
+ return ERR_PTR(-ENOMEM);
|
||||
+
|
||||
+ for (i = 0; i < count; i++) {
|
||||
+ if (copy_from_user(&fwb, &entry[i], sizeof(fwb))) {
|
||||
+ kfree(qs);
|
||||
+ return ERR_PTR(-EFAULT);
|
||||
+ }
|
||||
+
|
||||
+ qs[i].uaddr = compat_ptr(fwb.uaddr);
|
||||
+ qs[i].uval = fwb.val;
|
||||
+ qs[i].bitset = fwb.bitset;
|
||||
+ }
|
||||
+
|
||||
+ return qs;
|
||||
+}
|
||||
+
|
||||
SYSCALL_DEFINE6(futex_time32, u32 __user *, uaddr, int, op, u32, val,
|
||||
struct old_timespec32 __user *, utime, u32 __user *, uaddr2,
|
||||
u32, val3)
|
||||
@@ -4113,7 +4453,8 @@ SYSCALL_DEFINE6(futex_time32, u32 __user *, uaddr, int, op, u32, val,
|
||||
|
||||
if (utime && (cmd == FUTEX_WAIT || cmd == FUTEX_LOCK_PI ||
|
||||
cmd == FUTEX_WAIT_BITSET ||
|
||||
- cmd == FUTEX_WAIT_REQUEUE_PI)) {
|
||||
+ cmd == FUTEX_WAIT_REQUEUE_PI ||
|
||||
+ cmd == FUTEX_WAIT_MULTIPLE)) {
|
||||
if (get_old_timespec32(&ts, utime))
|
||||
return -EFAULT;
|
||||
if (!timespec64_valid(&ts))
|
||||
@@ -4128,6 +4469,19 @@ SYSCALL_DEFINE6(futex_time32, u32 __user *, uaddr, int, op, u32, val,
|
||||
cmd == FUTEX_CMP_REQUEUE_PI || cmd == FUTEX_WAKE_OP)
|
||||
val2 = (int) (unsigned long) utime;
|
||||
|
||||
+ if (cmd == FUTEX_WAIT_MULTIPLE) {
|
||||
+ int ret;
|
||||
+ struct futex_q *qs = compat_futex_read_wait_block(uaddr, val);
|
||||
+
|
||||
+ if (IS_ERR(qs))
|
||||
+ return PTR_ERR(qs);
|
||||
+
|
||||
+ ret = futex_wait_multiple(qs, op, val, tp);
|
||||
+ kfree(qs);
|
||||
+
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
return do_futex(uaddr, op, val, tp, uaddr2, val2, val3);
|
||||
}
|
||||
#endif /* CONFIG_COMPAT_32BIT_TIME */
|
||||
diff --git a/tools/testing/selftests/futex/functional/futex_wait_timeout.c b/tools/testing/selftests/futex/functional/futex_wait_timeout.c
|
||||
index ee55e6d389a3f053194435342c4e471dc7cf8786..2a63e1c2cfb6407a5988233217cff2e52787bc66 100644
|
||||
--- a/tools/testing/selftests/futex/functional/futex_wait_timeout.c
|
||||
+++ b/tools/testing/selftests/futex/functional/futex_wait_timeout.c
|
||||
@@ -11,6 +11,7 @@
|
||||
*
|
||||
* HISTORY
|
||||
* 2009-Nov-6: Initial version by Darren Hart <dvhart@linux.intel.com>
|
||||
+ * 2019-Dec-13: Add WAIT_MULTIPLE test by Krisman <krisman@collabora.com>
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
@@ -41,6 +42,8 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
futex_t f1 = FUTEX_INITIALIZER;
|
||||
struct timespec to;
|
||||
+ time_t secs;
|
||||
+ struct futex_wait_block fwb = {&f1, f1, 0};
|
||||
int res, ret = RET_PASS;
|
||||
int c;
|
||||
|
||||
@@ -65,7 +68,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
ksft_print_header();
|
||||
- ksft_set_plan(1);
|
||||
+ ksft_set_plan(2);
|
||||
ksft_print_msg("%s: Block on a futex and wait for timeout\n",
|
||||
basename(argv[0]));
|
||||
ksft_print_msg("\tArguments: timeout=%ldns\n", timeout_ns);
|
||||
@@ -79,8 +82,39 @@ int main(int argc, char *argv[])
|
||||
if (!res || errno != ETIMEDOUT) {
|
||||
fail("futex_wait returned %d\n", ret < 0 ? errno : ret);
|
||||
ret = RET_FAIL;
|
||||
+ } else
|
||||
+ ksft_test_result_pass("futex_wait timeout succeeds\n");
|
||||
+
|
||||
+ info("Calling futex_wait_multiple on f1: %u @ %p\n", f1, &f1);
|
||||
+
|
||||
+ /* Setup absolute time */
|
||||
+ ret = clock_gettime(CLOCK_REALTIME, &to);
|
||||
+ secs = (to.tv_nsec + timeout_ns) / 1000000000;
|
||||
+ to.tv_nsec = ((int64_t)to.tv_nsec + timeout_ns) % 1000000000;
|
||||
+ to.tv_sec += secs;
|
||||
+ info("to.tv_sec = %ld\n", to.tv_sec);
|
||||
+ info("to.tv_nsec = %ld\n", to.tv_nsec);
|
||||
+
|
||||
+ res = futex_wait_multiple(&fwb, 1, &to,
|
||||
+ FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME);
|
||||
+
|
||||
+#ifdef __ILP32__
|
||||
+ if (res == -1 && errno == ENOSYS) {
|
||||
+ ksft_test_result_skip("futex_wait_multiple not supported at x32\n");
|
||||
+ } else {
|
||||
+ ksft_test_result_fail("futex_wait_multiple returned %d\n",
|
||||
+ res < 0 ? errno : res);
|
||||
+ ret = RET_FAIL;
|
||||
}
|
||||
+#else
|
||||
+ if (!res || errno != ETIMEDOUT) {
|
||||
+ ksft_test_result_fail("futex_wait_multiple returned %d\n",
|
||||
+ res < 0 ? errno : res);
|
||||
+ ret = RET_FAIL;
|
||||
+ } else
|
||||
+ ksft_test_result_pass("futex_wait_multiple timeout succeeds\n");
|
||||
+#endif /* __ILP32__ */
|
||||
|
||||
- print_result(TEST_NAME, ret);
|
||||
+ ksft_print_cnts();
|
||||
return ret;
|
||||
}
|
||||
diff --git a/tools/testing/selftests/futex/include/futextest.h b/tools/testing/selftests/futex/include/futextest.h
|
||||
index ddbcfc9b7bac4aebb5bac2f249e26ecfd948aa84..bb103bef4557012ef9a389ca74c868e4476a8a31 100644
|
||||
--- a/tools/testing/selftests/futex/include/futextest.h
|
||||
+++ b/tools/testing/selftests/futex/include/futextest.h
|
||||
@@ -38,6 +38,14 @@ typedef volatile u_int32_t futex_t;
|
||||
#ifndef FUTEX_CMP_REQUEUE_PI
|
||||
#define FUTEX_CMP_REQUEUE_PI 12
|
||||
#endif
|
||||
+#ifndef FUTEX_WAIT_MULTIPLE
|
||||
+#define FUTEX_WAIT_MULTIPLE 13
|
||||
+struct futex_wait_block {
|
||||
+ futex_t *uaddr;
|
||||
+ futex_t val;
|
||||
+ __u32 bitset;
|
||||
+};
|
||||
+#endif
|
||||
#ifndef FUTEX_WAIT_REQUEUE_PI_PRIVATE
|
||||
#define FUTEX_WAIT_REQUEUE_PI_PRIVATE (FUTEX_WAIT_REQUEUE_PI | \
|
||||
FUTEX_PRIVATE_FLAG)
|
||||
@@ -80,6 +88,20 @@ futex_wait(futex_t *uaddr, futex_t val, struct timespec *timeout, int opflags)
|
||||
return futex(uaddr, FUTEX_WAIT, val, timeout, NULL, 0, opflags);
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * futex_wait_multiple() - block on several futexes with optional timeout
|
||||
+ * @fwb: wait block user space address
|
||||
+ * @count: number of entities at fwb
|
||||
+ * @timeout: absolute timeout
|
||||
+ */
|
||||
+static inline int
|
||||
+futex_wait_multiple(struct futex_wait_block *fwb, int count,
|
||||
+ struct timespec *timeout, int opflags)
|
||||
+{
|
||||
+ return futex(fwb, FUTEX_WAIT_MULTIPLE, count, timeout, NULL, 0,
|
||||
+ opflags);
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* futex_wake() - wake one or more tasks blocked on uaddr
|
||||
* @nr_wake: wake up to this many tasks
|
||||
diff --git a/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c b/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c
|
||||
index 0ae390ff816449c88d0bb655a26eb014382c2b4f..bcbac042992d447e0bc9ef5fefe94e875de310f2 100644
|
||||
--- a/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c
|
||||
+++ b/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c
|
||||
@@ -12,6 +12,7 @@
|
||||
*
|
||||
* HISTORY
|
||||
* 2009-Nov-14: Initial version by Gowrishankar <gowrishankar.m@in.ibm.com>
|
||||
+ * 2019-Dec-13: Add WAIT_MULTIPLE test by Krisman <krisman@collabora.com>
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
@@ -40,6 +41,7 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
struct timespec to = {.tv_sec = 0, .tv_nsec = timeout_ns};
|
||||
futex_t f1 = FUTEX_INITIALIZER;
|
||||
+ struct futex_wait_block fwb = {&f1, f1+1, 0};
|
||||
int res, ret = RET_PASS;
|
||||
int c;
|
||||
|
||||
@@ -61,7 +63,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
ksft_print_header();
|
||||
- ksft_set_plan(1);
|
||||
+ ksft_set_plan(2);
|
||||
ksft_print_msg("%s: Test the unexpected futex value in FUTEX_WAIT\n",
|
||||
basename(argv[0]));
|
||||
|
||||
@@ -71,8 +73,30 @@ int main(int argc, char *argv[])
|
||||
fail("futex_wait returned: %d %s\n",
|
||||
res ? errno : res, res ? strerror(errno) : "");
|
||||
ret = RET_FAIL;
|
||||
+ } else
|
||||
+ ksft_test_result_pass("futex_wait wouldblock succeeds\n");
|
||||
+
|
||||
+ info("Calling futex_wait_multiple on f1: %u @ %p with val=%u\n",
|
||||
+ f1, &f1, f1+1);
|
||||
+ res = futex_wait_multiple(&fwb, 1, NULL, FUTEX_PRIVATE_FLAG);
|
||||
+
|
||||
+#ifdef __ILP32__
|
||||
+ if (res != -1 || errno != ENOSYS) {
|
||||
+ ksft_test_result_fail("futex_wait_multiple returned %d\n",
|
||||
+ res < 0 ? errno : res);
|
||||
+ ret = RET_FAIL;
|
||||
+ } else {
|
||||
+ ksft_test_result_skip("futex_wait_multiple not supported at x32\n");
|
||||
+ }
|
||||
+#else
|
||||
+ if (!res || errno != EWOULDBLOCK) {
|
||||
+ ksft_test_result_fail("futex_wait_multiple returned %d\n",
|
||||
+ res < 0 ? errno : res);
|
||||
+ ret = RET_FAIL;
|
||||
}
|
||||
+ ksft_test_result_pass("futex_wait_multiple wouldblock succeeds\n");
|
||||
+#endif /* __ILP32__ */
|
||||
|
||||
- print_result(TEST_NAME, ret);
|
||||
+ ksft_print_cnts();
|
||||
return ret;
|
||||
}
|
||||
diff --git a/tools/testing/selftests/futex/functional/.gitignore b/tools/testing/selftests/futex/functional/.gitignore
|
||||
index a09f570619023750f558c84004aff166b4337d72..4660128a545edb04a17cc6bd9760931c1386122f 100644
|
||||
--- a/tools/testing/selftests/futex/functional/.gitignore
|
||||
+++ b/tools/testing/selftests/futex/functional/.gitignore
|
||||
@@ -5,3 +5,4 @@ futex_wait_private_mapped_file
|
||||
futex_wait_timeout
|
||||
futex_wait_uninitialized_heap
|
||||
futex_wait_wouldblock
|
||||
+futex_wait_multiple
|
||||
diff --git a/tools/testing/selftests/futex/functional/Makefile b/tools/testing/selftests/futex/functional/Makefile
|
||||
index 30996306cabcfe89a47977643e529b122893bb7e..75f9fface11fa3c90c1bdb9a49b3ea51291afd58 100644
|
||||
--- a/tools/testing/selftests/futex/functional/Makefile
|
||||
+++ b/tools/testing/selftests/futex/functional/Makefile
|
||||
@@ -14,7 +14,8 @@ TEST_GEN_FILES := \
|
||||
futex_requeue_pi_signal_restart \
|
||||
futex_requeue_pi_mismatched_ops \
|
||||
futex_wait_uninitialized_heap \
|
||||
- futex_wait_private_mapped_file
|
||||
+ futex_wait_private_mapped_file \
|
||||
+ futex_wait_multiple
|
||||
|
||||
TEST_PROGS := run.sh
|
||||
|
||||
diff --git a/tools/testing/selftests/futex/functional/futex_wait_multiple.c b/tools/testing/selftests/futex/functional/futex_wait_multiple.c
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..b48422e79f42edba1653bb0bd2a4c4fd98d2d48d
|
||||
--- /dev/null
|
||||
+++ b/tools/testing/selftests/futex/functional/futex_wait_multiple.c
|
||||
@@ -0,0 +1,173 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+/******************************************************************************
|
||||
+ *
|
||||
+ * Copyright © Collabora, Ltd., 2019
|
||||
+ *
|
||||
+ * DESCRIPTION
|
||||
+ * Test basic semantics of FUTEX_WAIT_MULTIPLE
|
||||
+ *
|
||||
+ * AUTHOR
|
||||
+ * Gabriel Krisman Bertazi <krisman@collabora.com>
|
||||
+ *
|
||||
+ * HISTORY
|
||||
+ * 2019-Dec-13: Initial version by Krisman <krisman@collabora.com>
|
||||
+ *
|
||||
+ *****************************************************************************/
|
||||
+
|
||||
+#include <errno.h>
|
||||
+#include <getopt.h>
|
||||
+#include <stdio.h>
|
||||
+#include <stdlib.h>
|
||||
+#include <string.h>
|
||||
+#include <time.h>
|
||||
+#include <pthread.h>
|
||||
+#include "futextest.h"
|
||||
+#include "logging.h"
|
||||
+
|
||||
+#define TEST_NAME "futex-wait-multiple"
|
||||
+#define timeout_ns 100000
|
||||
+#define MAX_COUNT 128
|
||||
+#define WAKE_WAIT_US 3000000
|
||||
+
|
||||
+int ret = RET_PASS;
|
||||
+char *progname;
|
||||
+futex_t f[MAX_COUNT] = {0};
|
||||
+struct futex_wait_block fwb[MAX_COUNT];
|
||||
+
|
||||
+void usage(char *prog)
|
||||
+{
|
||||
+ printf("Usage: %s\n", prog);
|
||||
+ printf(" -c Use color\n");
|
||||
+ printf(" -h Display this help message\n");
|
||||
+ printf(" -v L Verbosity level: %d=QUIET %d=CRITICAL %d=INFO\n",
|
||||
+ VQUIET, VCRITICAL, VINFO);
|
||||
+}
|
||||
+
|
||||
+void test_count_overflow(void)
|
||||
+{
|
||||
+ futex_t f = FUTEX_INITIALIZER;
|
||||
+ struct futex_wait_block fwb[MAX_COUNT+1];
|
||||
+ int res, i;
|
||||
+
|
||||
+ ksft_print_msg("%s: Test a too big number of futexes\n", progname);
|
||||
+
|
||||
+ for (i = 0; i < MAX_COUNT+1; i++) {
|
||||
+ fwb[i].uaddr = &f;
|
||||
+ fwb[i].val = f;
|
||||
+ fwb[i].bitset = 0;
|
||||
+ }
|
||||
+
|
||||
+ res = futex_wait_multiple(fwb, MAX_COUNT+1, NULL, FUTEX_PRIVATE_FLAG);
|
||||
+
|
||||
+#ifdef __ILP32__
|
||||
+ if (res != -1 || errno != ENOSYS) {
|
||||
+ ksft_test_result_fail("futex_wait_multiple returned %d\n",
|
||||
+ res < 0 ? errno : res);
|
||||
+ ret = RET_FAIL;
|
||||
+ } else {
|
||||
+ ksft_test_result_skip("futex_wait_multiple not supported at x32\n");
|
||||
+ }
|
||||
+#else
|
||||
+ if (res != -1 || errno != EINVAL) {
|
||||
+ ksft_test_result_fail("futex_wait_multiple returned %d\n",
|
||||
+ res < 0 ? errno : res);
|
||||
+ ret = RET_FAIL;
|
||||
+ } else {
|
||||
+ ksft_test_result_pass("futex_wait_multiple count overflow succeed\n");
|
||||
+ }
|
||||
+
|
||||
+#endif /* __ILP32__ */
|
||||
+}
|
||||
+
|
||||
+void *waiterfn(void *arg)
|
||||
+{
|
||||
+ int res;
|
||||
+
|
||||
+ res = futex_wait_multiple(fwb, MAX_COUNT, NULL, FUTEX_PRIVATE_FLAG);
|
||||
+
|
||||
+#ifdef __ILP32__
|
||||
+ if (res != -1 || errno != ENOSYS) {
|
||||
+ ksft_test_result_fail("futex_wait_multiple returned %d\n",
|
||||
+ res < 0 ? errno : res);
|
||||
+ ret = RET_FAIL;
|
||||
+ } else {
|
||||
+ ksft_test_result_skip("futex_wait_multiple not supported at x32\n");
|
||||
+ }
|
||||
+#else
|
||||
+ if (res < 0)
|
||||
+ ksft_print_msg("waiter failed %d\n", res);
|
||||
+
|
||||
+ info("futex_wait_multiple: Got hint futex %d was freed\n", res);
|
||||
+#endif /* __ILP32__ */
|
||||
+
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+void test_fwb_wakeup(void)
|
||||
+{
|
||||
+ int res, i;
|
||||
+ pthread_t waiter;
|
||||
+
|
||||
+ ksft_print_msg("%s: Test wake up in a list of futex\n", progname);
|
||||
+
|
||||
+ for (i = 0; i < MAX_COUNT; i++) {
|
||||
+ fwb[i].uaddr = &f[i];
|
||||
+ fwb[i].val = f[i];
|
||||
+ fwb[i].bitset = 0xffffffff;
|
||||
+ }
|
||||
+
|
||||
+ res = pthread_create(&waiter, NULL, waiterfn, NULL);
|
||||
+ if (res) {
|
||||
+ ksft_test_result_fail("Creating waiting thread failed");
|
||||
+ ksft_exit_fail();
|
||||
+ }
|
||||
+
|
||||
+ usleep(WAKE_WAIT_US);
|
||||
+ res = futex_wake(&(f[MAX_COUNT-1]), 1, FUTEX_PRIVATE_FLAG);
|
||||
+ if (res != 1) {
|
||||
+ ksft_test_result_fail("Failed to wake thread res=%d\n", res);
|
||||
+ ksft_exit_fail();
|
||||
+ }
|
||||
+
|
||||
+ pthread_join(waiter, NULL);
|
||||
+ ksft_test_result_pass("%s succeed\n", __func__);
|
||||
+}
|
||||
+
|
||||
+int main(int argc, char *argv[])
|
||||
+{
|
||||
+ int c;
|
||||
+
|
||||
+ while ((c = getopt(argc, argv, "cht:v:")) != -1) {
|
||||
+ switch (c) {
|
||||
+ case 'c':
|
||||
+ log_color(1);
|
||||
+ break;
|
||||
+ case 'h':
|
||||
+ usage(basename(argv[0]));
|
||||
+ exit(0);
|
||||
+ case 'v':
|
||||
+ log_verbosity(atoi(optarg));
|
||||
+ break;
|
||||
+ default:
|
||||
+ usage(basename(argv[0]));
|
||||
+ exit(1);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ progname = basename(argv[0]);
|
||||
+
|
||||
+ ksft_print_header();
|
||||
+ ksft_set_plan(2);
|
||||
+
|
||||
+ test_count_overflow();
|
||||
+
|
||||
+#ifdef __ILP32__
|
||||
+ // if it's a 32x binary, there's no futex to wakeup
|
||||
+ ksft_test_result_skip("futex_wait_multiple not supported at x32\n");
|
||||
+#else
|
||||
+ test_fwb_wakeup();
|
||||
+#endif /* __ILP32__ */
|
||||
+
|
||||
+ ksft_print_cnts();
|
||||
+ return ret;
|
||||
+}
|
||||
diff --git a/tools/testing/selftests/futex/functional/run.sh b/tools/testing/selftests/futex/functional/run.sh
|
||||
index 1acb6ace1680e8f3d6b3ee2dc528c19ddfdb018e..a8be94f28ff78b4879d2d19bca5d9b0fcb26c1f8 100755
|
||||
--- a/tools/testing/selftests/futex/functional/run.sh
|
||||
+++ b/tools/testing/selftests/futex/functional/run.sh
|
||||
@@ -73,3 +73,6 @@ echo
|
||||
echo
|
||||
./futex_wait_uninitialized_heap $COLOR
|
||||
./futex_wait_private_mapped_file $COLOR
|
||||
+
|
||||
+echo
|
||||
+./futex_wait_multiple $COLOR
|
||||
diff --git a/include/uapi/linux/futex.h b/include/uapi/linux/futex.h
|
||||
index 580001e89c6caed57dd8b3cb491d65dce846caff..a3e760886b8e7e74285fdcf2caaaa6f66ad16675 100644
|
||||
--- a/include/uapi/linux/futex.h
|
||||
+++ b/include/uapi/linux/futex.h
|
||||
@@ -21,7 +21,7 @@
|
||||
#define FUTEX_WAKE_BITSET 10
|
||||
#define FUTEX_WAIT_REQUEUE_PI 11
|
||||
#define FUTEX_CMP_REQUEUE_PI 12
|
||||
-#define FUTEX_WAIT_MULTIPLE 13
|
||||
+#define FUTEX_WAIT_MULTIPLE 31
|
||||
|
||||
#define FUTEX_PRIVATE_FLAG 128
|
||||
#define FUTEX_CLOCK_REALTIME 256
|
||||
diff --git a/kernel/futex.c b/kernel/futex.c
|
||||
index 58cf9eb2b851b4858e29b5ef4114a29a92e676ba..e0bb628a5e1988dcc9ae5442a4259edc229d578d 100644
|
||||
--- a/kernel/futex.c
|
||||
+++ b/kernel/futex.c
|
||||
@@ -4198,7 +4198,7 @@ SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,
|
||||
return -EINVAL;
|
||||
|
||||
t = timespec64_to_ktime(ts);
|
||||
- if (cmd == FUTEX_WAIT)
|
||||
+ if (cmd == FUTEX_WAIT || cmd == FUTEX_WAIT_MULTIPLE)
|
||||
t = ktime_add_safe(ktime_get(), t);
|
||||
tp = &t;
|
||||
}
|
||||
@@ -4399,6 +4399,7 @@ COMPAT_SYSCALL_DEFINE3(get_robust_list, int, pid,
|
||||
*/
|
||||
struct compat_futex_wait_block {
|
||||
compat_uptr_t uaddr;
|
||||
+ __u32 pad;
|
||||
__u32 val;
|
||||
__u32 bitset;
|
||||
};
|
||||
@@ -4461,7 +4462,7 @@ SYSCALL_DEFINE6(futex_time32, u32 __user *, uaddr, int, op, u32, val,
|
||||
return -EINVAL;
|
||||
|
||||
t = timespec64_to_ktime(ts);
|
||||
- if (cmd == FUTEX_WAIT)
|
||||
+ if (cmd == FUTEX_WAIT || cmd == FUTEX_WAIT_MULTIPLE)
|
||||
t = ktime_add_safe(ktime_get(), t);
|
||||
tp = &t;
|
||||
}
|
||||
|
38
playbook.yml
38
playbook.yml
|
@ -1,21 +1,23 @@
|
|||
---
|
||||
- hosts: buildservers
|
||||
become: true
|
||||
vars:
|
||||
fedora_version: f30
|
||||
patch_file: acso-5.0.5.patch # This patch has been the same since 4.18, renamed to appear more relevant
|
||||
fedora_version: f32
|
||||
patch_files:
|
||||
- acso-5.4.6.patch # This patch has been modified to work on 5.6+
|
||||
- fsync-5.6.patch
|
||||
run_epoch: "{{ ansible_date_time.epoch }}"
|
||||
build_dir: /tmp/{{ fedora_version }}_kernel
|
||||
tasks:
|
||||
- name: Ensure libselinux-python is installed
|
||||
dnf:
|
||||
name: libselinux-python
|
||||
state: present
|
||||
become: true
|
||||
# - name: Ensure libselinux-python is installed - no longer required on f31+?
|
||||
# dnf:
|
||||
# name: libselinux-python
|
||||
# state: present
|
||||
# become: true
|
||||
- name: Ensure fedpkg is installed
|
||||
dnf:
|
||||
name: fedpkg
|
||||
state: present
|
||||
become: true
|
||||
- name: Find old mock chroots
|
||||
find:
|
||||
paths: /var/lib/mock/
|
||||
|
@ -28,26 +30,32 @@
|
|||
path: "{{ item.path }}"
|
||||
state: absent
|
||||
with_items: "{{ chroots_to_delete.files }}"
|
||||
become: true
|
||||
- name: Clean old build dir
|
||||
file:
|
||||
path: "{{ build_dir }}"
|
||||
state: absent
|
||||
become: true
|
||||
- name: Clone {{ fedora_version }} kernel to {{ build_dir }}
|
||||
shell: fedpkg clone -a -b {{ fedora_version }} kernel {{ build_dir }}
|
||||
- name: Copy patch to build dir
|
||||
copy:
|
||||
src: payload/{{ patch_file }}
|
||||
src: "payload/{{ item }}"
|
||||
dest: "{{ build_dir }}"
|
||||
- name: Apply patch and build kernel (logs in {{ build_dir }}/results_kernel/*)
|
||||
with_items: "{{ patch_files }}"
|
||||
- name: Copy newpatch.sh on master branch, for 5.6 on f31
|
||||
copy:
|
||||
src: files/newpatch.sh
|
||||
mode: 0775
|
||||
dest: "{{ build_dir }}/scripts/"
|
||||
when: (fedora_version in ['master', 'f32'])
|
||||
- name: Apply patches
|
||||
command: "./scripts/newpatch.sh {{ item }} chdir={{ build_dir }}"
|
||||
with_items: "{{ patch_files }}"
|
||||
- name: Build kernel (logs in {{ build_dir }}/results_kernel/*)
|
||||
command: "{{ item }} chdir={{ build_dir }}"
|
||||
with_items:
|
||||
- ./scripts/newpatch.sh {{ patch_file }}
|
||||
- /usr/bin/sed -i -e 's/%define buildid .*$/%define buildid .acspatch/' kernel.spec
|
||||
- /usr/bin/sed -i -e 's/%define buildid .*$/%define buildid .acsfsync/' kernel.spec
|
||||
- /usr/bin/make release
|
||||
- fedpkg mockbuild
|
||||
become: true
|
||||
- name: Find RPMs in {{ build_dir }}/results_kernel/*/*/*.rpm
|
||||
find:
|
||||
paths: "{{ build_dir }}/results_kernel/"
|
||||
|
|
Loading…
Reference in a new issue