On 01/06/18(Fri) 19:13, Mark Kettenis wrote:
> The diff below changes our futex implementation to allow futexes that
> are shared between processes. Such futexes are a little bit tricky
> since there is no guarantee that a futex is mapped at the same address
> in each process. To solve tis issue I adopted the strategy chosen by
> FreeBSD. Shared memory objects have a uvm object associated with
> them. So instead of using the virtual address I can use the offset
> into that object. Together with the a pointer to the object itself
> that should form a unique ID for the futex.
Nice.
> In order to establish that we're dealing with shared memory we need to
> lookup the mapping corresponding to the userspace address of the
> futex. That involves locking the map. If we are worried about the
> additional overhead we could introduce shared versions of the futex
> operations and skip the lookup for non-shared (process-private) futexes.
>
> These futexes could be used to implement support for the POSIX Thread
> Process-Shared Synchronization option, i.e. the PTHREAD_PROCESS_SHARED
> attribute for mutexes. But I need these to make libxshmfence work on
> OpenBSD, which is in turn needed to get DRI3 support working.
>
> Making DRI3 support work is somewhat important. We're currently using
> DRI2 but Linux has moved to DRI3 and some of the DRI2-specific
> codepaths in X are starting to rot. DRI3 is also supposed to be more
> secure than DRI2. With DRI2, applications that can guess a 32-bit
> handle can in principle access graphiucs buffers from other
> applications. With DRI3 graphics buffers are shared using file
> descriptor passing which means that only processes that were passed
> the file descriptor can access the graphics buffer.
>
> Another feature of DRI3 is that graphics buffers can be shared between
> graphics devices. The idea there is that a GPU can render into a
> buffer which is then shared with the Intel CPU graphics which can then
> display it on the screen. But my initial implementation won't support
> that.
>
> Thoughts? Especially on whether we want to distinguish between
> process-shared and process-private futexes!
I'd prefer if we could do the distinction. Would it be enough to pass
a flag to futex_get()?
> Index: kern/sys_futex.c
> ===================================================================
> RCS file: /cvs/src/sys/kern/sys_futex.c,v
> retrieving revision 1.7
> diff -u -p -r1.7 sys_futex.c
> --- kern/sys_futex.c 24 Apr 2018 17:19:35 -0000 1.7
> +++ kern/sys_futex.c 1 Jun 2018 15:50:17 -0000
> @@ -31,6 +31,8 @@
> #include <sys/ktrace.h>
> #endif
>
> +#include <uvm/uvm_extern.h>
> +
> /*
> * Atomicity is only needed on MULTIPROCESSOR kernels. Fall back on
> * copyin(9) until non-MULTIPROCESSOR architectures have a copyin32(9)
> @@ -46,6 +48,8 @@
> struct futex {
> LIST_ENTRY(futex) ft_list; /* list of all futexes */
> TAILQ_HEAD(, proc) ft_threads; /* sleeping queue */
> + struct uvm_object *ft_obj;
> + voff_t ft_offset;
> uint32_t *ft_uaddr; /* userspace address */
> pid_t ft_pid; /* process identifier */
> unsigned int ft_refcnt; /* # of references */
> @@ -130,8 +134,22 @@ struct futex *
> futex_get(uint32_t *uaddr, int flag)
> {
> struct proc *p = curproc;
> + vm_map_t map = &p->p_vmspace->vm_map;
> + vm_map_entry_t entry;
> + struct uvm_object *obj = NULL;
> + voff_t offset = 0;
> struct futex *f;
>
> + vm_map_lock_read(map);
> + if (uvm_map_lookup_entry(map, (vaddr_t)uaddr, &entry)) {
> + if (entry->object.uvm_obj &&
> + entry->inheritance == MAP_INHERIT_SHARE) {
> + obj = entry->object.uvm_obj;
> + offset = entry->offset + ((vaddr_t)uaddr -
> entry->start);
> + }
> + }
> + vm_map_unlock_read(map);
> +
> rw_assert_wrlock(&ftlock);
>
> LIST_FOREACH(f, &ftlist, ft_list) {
> @@ -139,6 +157,10 @@ futex_get(uint32_t *uaddr, int flag)
> f->ft_refcnt++;
> break;
> }
> + if (f->ft_obj == obj && f->ft_offset == offset) {
> + f->ft_refcnt++;
> + break;
> + }
> }
>
> if ((f == NULL) && (flag & FT_CREATE)) {
> @@ -146,10 +168,15 @@ futex_get(uint32_t *uaddr, int flag)
> * We rely on the rwlock to ensure that no other thread
> * create the same futex.
> */
> - f = pool_get(&ftpool, PR_WAITOK);
> + f = pool_get(&ftpool, PR_WAITOK | PR_ZERO);
> TAILQ_INIT(&f->ft_threads);
> - f->ft_uaddr = uaddr;
> - f->ft_pid = p->p_p->ps_pid;
> + if (obj) {
> + f->ft_obj = obj;
> + f->ft_offset = offset;
> + } else {
> + f->ft_uaddr = uaddr;
> + f->ft_pid = p->p_p->ps_pid;
> + }
> f->ft_refcnt = 1;
> LIST_INSERT_HEAD(&ftlist, f, ft_list);
> }
>