The dumbest (futex-based) mutex
is this a correct mutex?


It is not correct. There is a requirement for std::mutex::lock (http://en.cppreference.com/w/cpp/thread/mutex/lock):


  If another thread has already locked the mutex, a call to lock will
  block execution until the lock is acquired.


A simple test below that uses your mutex consumes 100% CPU that means it is busy-waiting. I think you did it by mistake. 

On the contrary if I use std::mutex for the same test consumes 0% CPU in waiting on mt.lock().

mutex mt;
int main() {
    auto f = []() {
        mt.lock();
        mt.unlock();
    };
    mt.lock();
    std::thread t (f);
    std::this_thread::sleep_for(std::chrono::seconds(60));
    mt.unlock();
    t.join();
}


Below is a pidstat report for running a test with your mutex:

$ pidstat 1 -p $(pidof custom_mutex)  
Linux 3.10.0-229.el7.x86_64 (sk71.net.billing.ru)       05/16/2017      _x86_64_        (2 CPU)

09:44:31 AM   UID       PID    %usr %system  %guest    %CPU   CPU  Command
09:44:32 AM  1000      9806   11.00   88.00    0.00   99.00     0  custom_mutex
09:44:33 AM  1000      9806   14.00   86.00    0.00  100.00     0  custom_mutex
09:44:34 AM  1000      9806   13.00   86.00    0.00   99.00     0  custom_mutex
09:44:35 AM  1000      9806   15.00   84.00    0.00   99.00     0  custom_mutex
09:44:36 AM  1000      9806   15.00   83.00    0.00   98.00     0  custom_mutex
09:44:37 AM  1000      9806   12.00   87.00    0.00   99.00     0  custom_mutex
09:44:38 AM  1000      9806   16.00   83.00    0.00   99.00     0  custom_mutex
09:44:39 AM  1000      9806   15.00   85.00    0.00  100.00     0  custom_mutex


And this is a report for a test that uses std::mutex:

$ pidstat 1 -p $(pidof std_mutex)    
Linux 3.10.0-229.el7.x86_64 (sk71.net.billing.ru)       05/16/2017      _x86_64_        (2 CPU)

09:46:45 AM   UID       PID    %usr %system  %guest    %CPU   CPU  Command
09:46:46 AM  1000      9965    0.00    0.00    0.00    0.00     0  std_mutex
09:46:47 AM  1000      9965    0.00    0.00    0.00    0.00     0  std_mutex
09:46:48 AM  1000      9965    0.00    0.00    0.00    0.00     0  std_mutex
09:46:49 AM  1000      9965    0.00    0.00    0.00    0.00     0  std_mutex
09:46:50 AM  1000      9965    0.00    0.00    0.00    0.00     0  std_mutex
09:46:51 AM  1000      9965    0.00    0.00    0.00    0.00     0  std_mutex
09:46:52 AM  1000      9965    0.00    0.00    0.00    0.00     0  std_mutex
09:46:53 AM  1000      9965    0.00    0.00    0.00    0.00     0  std_mutex


update

I believe your mistake is in parameters for syscall. FUTEX_WAKE on my Linux is equal to 1. But take a look at:

inline int futex_wait(void *addr, int block_if_value_is) {
    return syscall(SYS_futex, addr, block_if_value_is, nullptr, nullptr, 0);
}


It passes block_if_value_is to SYS_futex as a parameter for int op. However the value of block_if_value_is in your program is equal to LOCKED and LOCKED is 1 (it means that it is equal to FUTEX_WAKE):

static constexpr int UNLOCKED = 0;
static constexpr int LOCKED = 1;

futex_wait(&amp;m_state, LOCKED);


So you do not wait. You actually ask the kernel to wake up 0 (nullptr) processes. That is why you instead of waiting in kernel on your futex  returns:

return syscall(SYS_futex, addr, block_if_value_is, nullptr, nullptr, 0);