Проблема в том, что вы загружаете mx_state в регистр (ограничение 'r'), а затем выполняете обмен с регистрами, записывая результат только в mx_state в конце кода asm. То, что вы хотите, это что-то вроде
asm(
"movl $0x01,%%eax\n\t" // move 1 to eax
"xchg %%eax,%1\n\t" // try to set the lock bit
"mov %%eax,%0\n\t" // export our result to a test var
:"=r"(failure)
:"m" (mutexlock->mx_state)
:"%eax"
);
Даже это несколько опасно, так как теоретически компилятор может загрузить mx_state, пролить его в слот локального временного стека и выполнить там xchg. Это также несколько неэффективно, поскольку в нем есть дополнительные жесткие коды, которые могут не понадобиться, но не могут быть устранены оптимизатором. Вам лучше использовать более простой asm, который расширяется до одной инструкции, такой как
failure = 1;
asm("xchg %0,0(%1)" : "=r" (failure) : "r" (&mutex->mx_state), "0" (failure));
Обратите внимание, как мы принудительно используем mx_state, используя его адрес, а не его значение.