[PATCH v8 2/4] arm64: vdso: Implement __vdso_futex_robust_try_unlock()

André Almeida andrealmeid at igalia.com
Wed Sep 16 18:06:25 PDT 2026


Em 16/09/2026 11:27, Mark Rutland escreveu:
> Hi André,
> 
> I have a few comments below. I'm not sure what the plan is for 32-bit
> robust lists, and I think there are a few problems with the assembly.
> 
> On Fri, Aug 21, 2026 at 06:50:43PM -0300, André Almeida wrote:

[...]

> 
>> +	register __u64 *pop_reg asm("x2") = pop;
>> +	register __u32 result_reg asm("w3") = 0;
>> +	__u32 val;
>> +
>> +	asm volatile (
>> +		".globl						  "
>> +		"__futex_list64_try_unlock_cs_start,		  "
>> +		"__futex_list64_try_unlock_cs_success,		  "
>> +		"__futex_list64_try_unlock_cs_end		\n"
> 
> Sorry, when I mentioned putting these within the assembly, I meant doing
> something like:
> 
> | #define LABEL(l)						\
> | 	"	.globl " #l "\n"				\
> | 	#l ":\n"
> 
> ... and within the asssembly, having:
> 
> | 	"	insn1					\n"
> | 	"	insn2					\n"
> | 	LABEL(__futex_list64_try_unlock_cs_start)
> | 	"	insn2					\n"
> | 	LABEL(__futex_list64_try_unlock_cs_start)
> | 	"	insn4					\n"
> | 	LABEL(__futex_list64_try_unlock_cs_success)
> | 	"	insn5					\n"
> 
> That way we use the full strings, which are easy to grep for, and we
> only have to define each label string once.

Oh, I see, thanks! I will change that for the next version.

> 
>> +
>> +		"	prfm pstl1strm, %[lock]			\n"
>> +		"retry:						\n"
>> +		"	ldxr %w[val], %[lock]			\n"
>> +		"	cmp %w[tid], %w[val]			\n"
>> +		"	bne __futex_list64_try_unlock_cs_end	\n"
> 
> The 'bne' here should be 'b.ne'. IIUC assemblers accept the former by
> historical accident, and 'b.<cond>' is the architecturally defined
> mnemonic.
> 
> As general thing, we format assembly with a tab between the instruction
> and first operand, so the above should be:
> 
> | 	"	prfm	pstl1strm, %[lock]			\n"
> | 	"retry:							\n"
> | 	"	ldxr	%w[val], %[lock]			\n"
> | 	"	cmp	%w[tid], %w[val]			\n"
> | 	"	b.ne	__futex_list64_try_unlock_cs_end	\n"
> 
>> +		"	stlxr %w[result], wzr, %[lock]		\n"
>> +		"__futex_list64_try_unlock_cs_start:		\n"
>> +		"	cbnz %w[result], retry			\n"
>> +		"__futex_list64_try_unlock_cs_success:		\n"
>> +		"	str xzr, %[pop_reg]			\n"
>> +		"__futex_list64_try_unlock_cs_end:		\n"
>> +
>> +		: [val] "=&r" (val), [result] "=&r" (result_reg), [pop_reg] "+Q" (*pop_reg)
>> +		: [tid] "r" (tid), [lock] "Q" (*lock)
>> +		: "cc", "memory"
>> +	);
> 
> As a general note, for assembly constaints, please put each constraint
> on its own line, e.g.
> 
> 	: [val] "=&r" (val),
> 	  [result] "=&r" (result_reg),
> 	  [pop_reg] "+Q" (*pop_reg)
> 	: [tid] "r" (tid),
> 	  [lock] "Q" (*lock)
> 	: "cc", "memory"
> 
> That formatting makes it much easier to read each constraint
> individually, and it makes it easier to review changes to individual
> constaints.
> 
> Generally, for asm we have a preferred style:
> 
> 	asm volatile(
> 	"	// string starts aligned with 'asm'	\n"
> 	"	// with 1 tab indend within that.	\n"
> 	"						\n"
> 	"	insn	operand1, operand2		\n"
> 	"label:						\n"
> 	"	insn	operand1, operand2		\n"
> 	: [output1] "=r" (...),
> 	  [output2] "=r" (...)
> 	: [input1] "r" (...).
> 	  [input2] "r" (...)
> 	: "clobbers"
> 	);
> 
> I see a couple of problems with the constraints as-is.
> 
> Firstly, the constraints for [pop_reg] doesn't guarantee that x2 is
> used. The "+Q" constraint takes a memory operand (in this case the
> location pointed to by 'pop_reg', and limits the addressing mode to a
> single base register with no offset. The operand is the memroy location,
> not the register holding the memory location, so this constraint won't
> necessarily use x2. Unless 'pop_reg' itself is passed into a register
> constraint, the compiler might not ensure that 'x2' is populated.
> 
> For example:
> 
> | [mark at gravadlaks:~/tests/asm-operands-q-reg]% cat test.c
> | unsigned long foo(unsigned long *ptr)
> | {
> |         register unsigned long *ptr_reg asm("x2") = ptr;
> |         unsigned long val;
> |
> |         asm volatile(
> |         "       ldr     %[val], %[qptr]\n"
> |         : [val] "=&r" (val)
> |         : [qptr] "Q" (*ptr_reg)
> |         );
> |
> |         return val;
> | }
> | [mark at gravadlaks:~/tests/asm-operands-q-reg]% usekorg 16.2.0 aarch64-linux-gcc -c test.c -O2
> | [mark at gravadlaks:~/tests/asm-operands-q-reg]% usekorg 16.2.0 aarch64-linux-objdump -d test.o
> |
> | test.o:     file format elf64-littleaarch64
> |
> |
> | Disassembly of section .text:
> |
> | 0000000000000000 <foo>:
> |    0:   f9400001        ldr     x1, [x0]
> |    4:   aa0103e0        mov     x0, x1
> |    8:   d65f03c0        ret
> 
> We will need an "r" input constraint for 'pop_reg' to ensure that x2 is
> allocated. Either in addition to an "=Q" output constraint for the
> actual memory write, or we rely on the memory clobber to hazaard against
> other memory accesses.
> 
> Secondly, the "Q" input constaint for 'lock' looks inaccurate, given
> that we both read and write to 'lock'. Either that should be a "+Q"
> output constraint, or we rely on the memory clobber to hazard against
> other memory accesses.
> 

Thank you very much Mark, that is a lot of good information about 
writing arm64 asm!

If I understood correctly, the correct approach here would be to use 
pop_reg as an input operand, making sure that we store the address hold 
by *pop on x2 register right? So something along the lines:

__u32 __vdso_futex_robust_list64_try_unlock(__u32 *lock, __u32 tid, 
__u64 *pop)
{
	register __u64 pop_reg asm("x2") = (__u64) pop;

...

		"	str 	xzr, [%x[pop_reg]]		\n"
...

		: [val] "=&r" (val),
		  [result] "=&r" (result_reg),
		  [lock] "+Q" (*lock)
		: [tid] "r" (tid),
		  [pop_reg] "r" (pop_reg)
		: "cc", "memory"

Does that looks right?

Thanks again for the help


> Mark.
> 
>> +
>> +	return val;
>> +}
>>
>> -- 
>> 2.55.0
>>




More information about the linux-arm-kernel mailing list