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.

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

Thoughts?  Especially on whether we want to distinguish between
process-shared and process-private futexes!

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>
+#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 - 
+               }
+       }
+       vm_map_unlock_read(map);
        LIST_FOREACH(f, &ftlist, ft_list) {
@@ -139,6 +157,10 @@ futex_get(uint32_t *uaddr, int flag)
+               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);
-               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);

Reply via email to