Skip to content

Commit be074eb

Browse files
andrealmeidkakra
authored andcommitted
futex: Add entry point for FUTEX_WAIT_MULTIPLE (opcode 31)
Add an option to wait on multiple futexes using the old interface, that uses opcode 31 through futex() syscall. Do that by just translation the old interface to use the new code. This allows old and stable versions of Proton to still use fsync in new kernel releases. Signed-off-by: André Almeida <[email protected]>
1 parent 7506dd1 commit be074eb

File tree

2 files changed

+87
-1
lines changed

2 files changed

+87
-1
lines changed

include/uapi/linux/futex.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#define FUTEX_WAIT_REQUEUE_PI 11
2323
#define FUTEX_CMP_REQUEUE_PI 12
2424
#define FUTEX_LOCK_PI2 13
25+
#define FUTEX_WAIT_MULTIPLE 31
2526

2627
#define FUTEX_PRIVATE_FLAG 128
2728
#define FUTEX_CLOCK_REALTIME 256
@@ -68,6 +69,18 @@ struct futex_waitv {
6869
__u32 __reserved;
6970
};
7071

72+
/**
73+
* struct futex_wait_block - Block of futexes to be waited for
74+
* @uaddr: User address of the futex
75+
* @val: Futex value expected by userspace
76+
* @bitset: Bitset for the optional bitmasked wakeup
77+
*/
78+
struct futex_wait_block {
79+
__u32 __user *uaddr;
80+
__u32 val;
81+
__u32 bitset;
82+
};
83+
7184
/*
7285
* Support for robust futexes: the kernel cleans up held futexes at
7386
* thread exit time.

kernel/futex/syscalls.c

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ static __always_inline bool futex_cmd_has_timeout(u32 cmd)
142142
case FUTEX_LOCK_PI2:
143143
case FUTEX_WAIT_BITSET:
144144
case FUTEX_WAIT_REQUEUE_PI:
145+
case FUTEX_WAIT_MULTIPLE:
145146
return true;
146147
}
147148
return false;
@@ -154,13 +155,79 @@ futex_init_timeout(u32 cmd, u32 op, struct timespec64 *ts, ktime_t *t)
154155
return -EINVAL;
155156

156157
*t = timespec64_to_ktime(*ts);
157-
if (cmd == FUTEX_WAIT)
158+
if (cmd == FUTEX_WAIT || cmd == FUTEX_WAIT_MULTIPLE)
158159
*t = ktime_add_safe(ktime_get(), *t);
159160
else if (cmd != FUTEX_LOCK_PI && !(op & FUTEX_CLOCK_REALTIME))
160161
*t = timens_ktime_to_host(CLOCK_MONOTONIC, *t);
161162
return 0;
162163
}
163164

165+
/**
166+
* futex_read_wait_block - Read an array of futex_wait_block from userspace
167+
* @uaddr: Userspace address of the block
168+
* @count: Number of blocks to be read
169+
*
170+
* This function creates and allocate an array of futex_q (we zero it to
171+
* initialize the fields) and then, for each futex_wait_block element from
172+
* userspace, fill a futex_q element with proper values.
173+
*/
174+
inline struct futex_vector *futex_read_wait_block(u32 __user *uaddr, u32 count)
175+
{
176+
unsigned int i;
177+
struct futex_vector *futexv;
178+
struct futex_wait_block fwb;
179+
struct futex_wait_block __user *entry =
180+
(struct futex_wait_block __user *)uaddr;
181+
182+
if (!count || count > FUTEX_WAITV_MAX)
183+
return ERR_PTR(-EINVAL);
184+
185+
futexv = kcalloc(count, sizeof(*futexv), GFP_KERNEL);
186+
if (!futexv)
187+
return ERR_PTR(-ENOMEM);
188+
189+
for (i = 0; i < count; i++) {
190+
if (copy_from_user(&fwb, &entry[i], sizeof(fwb))) {
191+
kfree(futexv);
192+
return ERR_PTR(-EFAULT);
193+
}
194+
195+
futexv[i].w.flags = FUTEX_32;
196+
futexv[i].w.val = fwb.val;
197+
futexv[i].w.uaddr = (uintptr_t) (fwb.uaddr);
198+
futexv[i].q = futex_q_init;
199+
}
200+
201+
return futexv;
202+
}
203+
204+
int futex_wait_multiple(struct futex_vector *vs, unsigned int count,
205+
struct hrtimer_sleeper *to);
206+
207+
int futex_opcode_31(ktime_t *abs_time, u32 __user *uaddr, int count)
208+
{
209+
int ret;
210+
struct futex_vector *vs;
211+
struct hrtimer_sleeper *to = NULL, timeout;
212+
213+
to = futex_setup_timer(abs_time, &timeout, 0, 0);
214+
215+
vs = futex_read_wait_block(uaddr, count);
216+
217+
if (IS_ERR(vs))
218+
return PTR_ERR(vs);
219+
220+
ret = futex_wait_multiple(vs, count, abs_time ? to : NULL);
221+
kfree(vs);
222+
223+
if (to) {
224+
hrtimer_cancel(&to->timer);
225+
destroy_hrtimer_on_stack(&to->timer);
226+
}
227+
228+
return ret;
229+
}
230+
164231
SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,
165232
const struct __kernel_timespec __user *, utime,
166233
u32 __user *, uaddr2, u32, val3)
@@ -180,6 +247,9 @@ SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,
180247
tp = &t;
181248
}
182249

250+
if (cmd == FUTEX_WAIT_MULTIPLE)
251+
return futex_opcode_31(tp, uaddr, val);
252+
183253
return do_futex(uaddr, op, val, tp, uaddr2, (unsigned long)utime, val3);
184254
}
185255

@@ -373,6 +443,9 @@ SYSCALL_DEFINE6(futex_time32, u32 __user *, uaddr, int, op, u32, val,
373443
tp = &t;
374444
}
375445

446+
if (cmd == FUTEX_WAIT_MULTIPLE)
447+
return futex_opcode_31(tp, uaddr, val);
448+
376449
return do_futex(uaddr, op, val, tp, uaddr2, (unsigned long)utime, val3);
377450
}
378451
#endif /* CONFIG_COMPAT_32BIT_TIME */

0 commit comments

Comments
 (0)