axboe / liburing

Library providing helpers for the Linux kernel io_uring support
MIT License
2.72k stars 393 forks source link

Can't find `futex2(2)` manual #1072

Closed YoSTEALTH closed 5 months ago

YoSTEALTH commented 5 months ago

I been looking all over for futex2(2) manual, referenced in man io_uring_prep_futex_*, ... I can't find it. I am trying to define proper futex_flags.

axboe commented 5 months ago

It's a bit confusing because futex2 is really just a modified futex API. In particular, things like futex waitv falls under futex2.

Any of the flags that are named FUTEX2_* are the ones you can pass in ->futex_flags, see:

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/futex.h#n46

for example. These all/mostly have mirrors in the regular futex flags. Outside of that, the API can just be considered as described in futex(2).

YoSTEALTH commented 5 months ago

Linux 6.7.4

I added the basic defines: https://github.com/YoSTEALTH/Liburing/blob/master/src/liburing/lib/futex.pxd

I think it would be nice for define names to be similar, either all the defines should be FUTEX_* or FUTEX2_*, having both futex and futex2 is just confusing.

I have also re-coded couple of C test into Python from test/futex.c:

  1. This test works fine:

def test_multi_wake():
    val = 0
    mask = liburing.FUTEX_BITSET_MATCH_ANY
    futex = array.array('I', [0])
    futex_flags = liburing.FUTEX2_SIZE_U32

    ring = liburing.io_uring()
    cqe = liburing.io_uring_cqe()
    try:
        assert liburing.io_uring_queue_init(8, ring) == 0

        # Submit two futex waits
        sqe = liburing.io_uring_get_sqe(ring)
        liburing.io_uring_prep_futex_wait(sqe, futex, val, mask, futex_flags)
        sqe.user_data = 1

        sqe = liburing.io_uring_get_sqe(ring)
        liburing.io_uring_prep_futex_wait(sqe, futex, val, mask, futex_flags)
        sqe.user_data = 2

        assert liburing.io_uring_submit(ring) == 2

        # Now submit wake for just one futex
        futex[0] = 1
        sqe = liburing.io_uring_get_sqe(ring)
        liburing.io_uring_prep_futex_wake(sqe, futex, 2, mask, futex_flags)
        sqe.user_data = 100

        assert liburing.io_uring_submit(ring) == 1

        # We expect to find completions for the both futex waits, and the futex wake.
        for i in range(3):
            assert liburing.io_uring_wait_cqe(ring, cqe) == 0
            assert cqe.res >= 0
            liburing.io_uring_cqe_seen(ring, cqe)
        assert liburing.io_uring_peek_cqe(ring, cqe) == -errno.EAGAIN
    finally:
        liburing.io_uring_queue_exit(ring)
  1. This test fails cqe.user_data != 2:

    def test_futex_wake_zero():
    val = 0
    mask = liburing.FUTEX_BITSET_MATCH_ANY
    futex = array.array('I', [0])
    futex_flags = liburing.FUTEX2_SIZE_U32
    
    ring = liburing.io_uring()
    cqe = liburing.io_uring_cqe()
    try:
        assert liburing.io_uring_queue_init(8, ring) == 0
    
        sqe = liburing.io_uring_get_sqe(ring)
        liburing.io_uring_prep_futex_wait(sqe, futex, val, mask, futex_flags)
        sqe.user_data = 1
        assert liburing.io_uring_submit(ring) == 1
    
        sqe = liburing.io_uring_get_sqe(ring)
        liburing.io_uring_prep_futex_wake(sqe, futex, val, mask, futex_flags)
        sqe.user_data = 2
        assert liburing.io_uring_submit(ring) == 1
    
        assert liburing.io_uring_wait_cqe(ring, cqe) == 0
    
        # Should get zero res and it should be the wake
        assert cqe.res == 0
        assert cqe.user_data != 2  # *** This is where it fails `2 != 2` ***
    
        liburing.io_uring_cqe_seen(ring, cqe)
    
        # Should not have the wait complete
        with pytest.raises(BlockingIOError):
            liburing.trap_error(liburing.io_uring_peek_cqe(ring, cqe))
    finally:
        del futex
        liburing.io_uring_queue_exit(ring)

    This is where the C code is, https://github.com/axboe/liburing/blob/master/test/futex.c#L363-L365 logic doesn't match comment? && vs ||?

axboe commented 5 months ago

You probably got the wait completion first? You seem to be making assumptions about the wake posting first, and the above sequence will post two of them.

It is a logic error in the test, but it'll pass just fine with && as well.

YoSTEALTH commented 5 months ago

Ya, going with your comment, this works:

# Should get zero res and it should be the wake
assert cqe.res == 0 and cqe.user_data == 2
YoSTEALTH commented 5 months ago

FUTEX_CLOCK_REALTIME is not used or supported by io_uring right?

YoSTEALTH commented 5 months ago

Not sure why this is closed! futex2 manual is still needed to be created though or io_uring_futex_* manual needs to be improved... futex2 being added into io_uring is an amazing thing. I can see lots of people using it, better to be prepared vs everyone having to spends days readings bit here and there to figure out how to actually use it.

Thanks Jens and sorry to be nitpicking.

axboe commented 5 months ago

I'm not going to write the futex2(2) man page, and the guy behind it is on leave right now. I'll be happy to add the relevant futex flags to the liburing man page, though.

YoSTEALTH commented 5 months ago

I'm not going to write the futex2(2) man page, and the guy behind it is on leave right now.

I see, makes sense.

I'll be happy to add the relevant futex flags to the liburing man page, though.

Cool.

I found 6.8.0-rc6 https://docs.kernel.org/userspace-api/futex2.html in it mentioned "32 bit sized futex" being supported . Clearly in futex.h there is FUTEX2_SIZE_U8, FUTEX2_SIZE_U16, FUTEX2_SIZE_U32, ... confusing.

Is there any advantage to using io_uring_prep_futex_waitv the interface with struct and having to pass external managed futex_state(available, unavailable) is quite cumbersome.