In the linux kernel code, when a spinlock is locked, the spin_lock function will spinning. The code of spin_lock is below:
static __always_inline void __ticket_spin_lock(raw_spinlock_t *lock)
{
int inc = 0x00010000;
int tmp;
asm volatile(LOCK_PREFIX "xaddl %0, %1\n"
"movzwl %w0, %2\n\t"
"shrl $16, %0\n\t"
"1:\t"
"cmpl %0, %2\n\t"
"je 2f\n\t"
"rep ; nop\n\t"
"movzwl %1, %2\n\t"
/* don't need lfence here, because loads are in-order */
"jmp 1b\n"
"2:"
: "+r" (inc), "+m" (lock->slock), "=&r" (tmp)
:
: "memory", "cc");
}
My question is:
How can I add a time counter to monitor the spinning time of the lock?Please give me some advice.
You can use rdtsc time stamp counter to measure the interval ,you can view the below links http://www.xml.com/ldd/chapter/book/ch06.html
http://wiki.osdev.org/Inline_Assembly/Examples
Related
I've been trying to implement a gcc inline function (AT&T assembly) that will perform an atomic CAS operation, but I can't get it to work - the return value is always getting messed up.
I've tried 2 different approaches, each seems to have its own misbehaviours:
1.
static inline int
cas(volatile void * addr, int expected, int newval)
{
int result = 1;
asm volatile("lock; cmpxchgl %3, (%2)\n\t"
"pushfl\n\t"
"popl %%ebx\n\t"
"andl $0x40, %%ebx\n\t"
"cmpl $0x0, %%ebx\n\t"
"jnz res%=\n\t"
"movl $0, %0\n\t"
"res%=:\n\t"
: "=m"(result)
: "a"(expected), "b"(addr), "r"(newval)
: "memory");
return result;
}
2.
static inline int cas(volatile void * addr, int expected, int newval) {
int ret = 1;
asm volatile("lock; cmpxchgl %3, (%2)\n\t"
"jz cas_success%=\n\t"
"movl $0, %0\n\t"
"cas_success%=:\n\t"
: "=m"(ret)
: "a"(expected), "b"(addr), "r"(newval)
: "memory");
return ret;
}
But neither work, could anyone point me at the problem with one of the implementations?
Thanks
I have a code in C where the main tak is written in Assembly. The idea of the programm is to for example when x = abc def ,and y = a it deletes the word where at least one letter is the same and writes words without the same letters so it would write def. I have wrotten a code, but it gives error like :
prog.c:10: Error: no such instruction: `addl $112,%esp'
prog.c:12: Error: no such instruction: `xorl %eax,%eax'
prog.c:13: Error: no such instruction: `popl %ebx'
prog.c:16: Error: no such instruction: `popl %esi'
Here is the code :
#include <stdio.h>
#include <string.h>
int main(){
char *x = "asbc4a2bab ";
char *y = "ab";
char bufor[100];
asm volatile (
".intel_syntax noprefix;"
"mov ecx,%0;"
"push ecx;" //wrzuca na stos
"mov ecx,%1;"
"mov eax,%2;"
"call zadanie1;"
"jmp wyjscie;"
"zadanie1:"
"push ebp;" //wrzucamy ebp na stos
"push eax;"
"push ecx;" //ecx zliczanie
"push edi;" //edi destination
"mov ebp,esp;" //do ebp adres stosu
"mov esi,[ebp+20];" //esi bezposrednio do x
"mov edi,[ebp+4];" //edi adres y
"mov ebx,[ebp+8];"//ebx bufor
"mov eax,0;"//eax to false
"push eax;"
"push esi;"
"push eax;"
"etykieta_x:"
"mov eax,[esp+8];"
"cmp eax,0;"
"je etykieta_y;"
"mov [esp+4],esi;"
"mov eax,0;"
"mov [esp+8],eax;"
//"mov [esp+4],esi;"
"etykieta_y:"
"mov eax,[edi];"
"cmp eax,'\0';" //porownoje eax z koncem
"je koniec_etykiety_x;"
"add edi,1;"//zwiekszamy petle
"cmp eax,[esi];"//porownoje y i x
"jne etykieta_y;"//wrocimy do etykiety y jesli nie sa rowne
"ustaw_flage:"
"pop eax;"
"mov eax,1;" //ustawia flage
"push eax;"
"koniec_etykiety_x:"
"pop eax;"
"cmp eax,1;"
"jne iteruj_dalej;"
"mov eax,0;"
"push eax;"
"iteruj_po_znakach:"
"add esi,1;"
"mov eax,[esi];"
"cmp eax,'\0';"
"je koniec;"
"cmp eax,' ';"
"je spacja_wykryta;"
"jmp etykieta_x;"
"spacja_wykryta:"
"mov eax,1;"
"mov [esp+8],eax;"
"jmp iteruj_po_znakach;"
"iteruj_dalej:"
"mov eax,0;"
"push eax;"
"add esi,1;"//zwiekszamy adres
"mov eax,[esi];"//pobieramhy nast zznak
"cmp eax,'\0';"
"je zapisz_do_bufora;"
"cmp eax,' ';"
"je spacja_wykryta_2;"
"mov eax,[esp+8];"
"cmp eax,0;"
"je etykieta_x;"
"jmp zapisz_do_bufora;"
"spacja_wykryta_2:"
"mov eax,1;"
"mov [esp+8],eax;"
"jmp iteruj_dalej;"
"zapisz_do_bufora:"
"mov eax,[esp+4];"
"interuj_po_slowie:"
"mov edx,[eax];"
"cmp edx,' ';"
"je etykieta_x;"
"cmp edx, '\0';"
"je etykieta_x;"
"mov [ebx],edx;"
"add eax,1;"
"add ebx,1;"
"jmp iteruj_po_slowie;"
"koniec:"
"pop edi;" //zdejmuje ze stosu
"pop ecx;"
"pop eax;"
"pop ebp;"
"ret;" //wyjdzie z funkcji
"wyjscie:"
".att_syntax_prefix;"
:
:"r"(x), "r"(y), "r"(bufor)
:"eax", "ecx"
);
return 0;
}
and here is the ideone link : http://ideone.com/wHFeDK
Someone know what may be wrong ? Thanks for help.
It's a horrible hack to manually switch syntax mode in inline asm and it might not work if you have any argument substitutions. The correct way is to use -masm=intel if you want intel syntax.
That said, your problem is that you have a typo in the directive where you wanted to restore the mode: you have .att_syntax_prefix instead of .att_syntax prefix (notice you don't need another underscore before prefix).
Also, the '\0' won't work, you should just use 0.
And, you have a typo: interuj_po_slowie vs iteruj_po_slowie.
PS: Stackoverflow is an english language forum, please post code in english, and comment it properly.
I'm trying to learn how to use inline assembly in C code.
I have created a small program that should add two integers:
int main(){
int a=1;
int b=2;
asm( "movl %0, %%r8d;"
"movl %1, %%r9d;"
"addl %%r8d, %%r9d;"
"movl %%r9d, %1;"
: "=r" (a)
: "r" (b)
:"%r8","%r9" );
printf("a=%d\n",a);
return 0;
}
The aim was to load a and b into the registers %r8 and %r9, add them, and then put the output back in a.
However this program prints a=2 instead a=3. I'm not sure if the problem is in the inline technique or in the assembly itself.
There are two issues here:
First: The "=r" constraint you use for the output operand a indicates to the compiler that the operand is write-only — it is allowed to assume that the initial value is not needed. This is definitely not the case for your code! Change the qualifier to "+r" to let the compiler know that the initial value is important.
Second: You are moving the result to the wrong register! The target %1 of the last movl is the register corresponding to b, not a. You want %0.
Fixed:
asm(
"movl %0, %%r8d;"
"movl %1, %%r9d;"
"addl %%r8d, %%r9d;"
"movl %%r9d, %0;"
: "+r" (a)
: "r" (b)
: "%r8", "%r9"
);
I read the spinlock function code in the linux kernel. There are two functions related to spinlock. See the code below:
static __always_inline void __ticket_spin_lock(raw_spinlock_t *lock)
{
short inc = 0x0100;
asm volatile (
LOCK_PREFIX "xaddw %w0, %1\n"
"1:\t"
"cmpb %h0, %b0\n\t"
"je 2f\n\t"
"rep ; nop\n\t"
"movb %1, %b0\n\t"
/* don't need lfence here, because loads are in-order */
"jmp 1b\n"
"2:"
: "+Q" (inc), "+m" (lock->slock)
:
: "memory", "cc");
}
static __always_inline void __ticket_spin_lock(raw_spinlock_t *lock)
{
int inc = 0x00010000;
int tmp;
asm volatile(LOCK_PREFIX "xaddl %0, %1\n"
"movzwl %w0, %2\n\t"
"shrl $16, %0\n\t"
"1:\t"
"cmpl %0, %2\n\t"
"je 2f\n\t"
"rep ; nop\n\t"
"movzwl %1, %2\n\t"
/* don't need lfence here, because loads are in-order */
"jmp 1b\n"
"2:"
: "+r" (inc), "+m" (lock->slock), "=&r" (tmp)
:
: "memory", "cc");
}
I have two question:
1.What's the difference between the two functions above?
2.What can I do to monitor the spinlock waiting time(the time it takes to first try the lock and finally get the lock)?does the variable inc means the spinlock waiting time?
Let me first explain how the spinlock code works. We have variables
uint16_t inc = 0x0100,
lock->slock; // I'll just call this "slock"
In the assembler code, inc is referred to as %0 and slock as %1. Moreover, %b0 denotes the lower 8 bit, i.e. inc % 0x100, and %h0 is inc / 0x100.
Now:
lock xaddw %w0, %1 ;; "inc := slock" and "slock := inc + slock"
;; simultaneously (atomic exchange and increment)
1:
cmpb %h0, %b0 ;; "if (inc / 256 == inc % 256)"
je 2f ;; " goto 2;"
rep ; nop ;; "yield();"
movb %1, %b0 ;; "inc = slock;"
jmp 1b ;; "goto 1;"
2:
Comparing the upper and lower byte of inc succeeds if inc is zero. Since inc has the value of the original lock, this happens if the lock is unlocked. In that case, the lock will already have been incremented to non-zero by the atomic exchange-and-increment, so it is now locked.
Otherwise, i.e. if the lock had already been locked, we pause a little, then update inc to the current value of the lock, and try again.
(I believe there's actually a possiblity for an overflow, if 28 threads simultaneously attempt to get the spinlock. In that case, slock is updated to 0x0100, 0x0200, ... 0xFF00, 0x0000, and would then appear to be unlocked. Maybe that's why the second version of the code uses a 16-bit wide counter, which would require 216 simultaneous attempts.)
Now let's insert a counter:
uint32_t spincounter = 0;
asm volatile( /* code below */
: "+Q" (inc), "+m" (lock->slock)
: "=r" (spincounter)
: "memory", "cc");
Now spincounter may be referred to as %2. We just need to increment the counter each time:
1:
inc %2
cmpb %h0, %b0
;; etc etc
I haven't tested this, but that's the general idea.
I'm doing a study assignment to measure memory access time on my machine.
To determine the clock cycles on our machines, we have been given the following C snippet:
static inline void getcyclecount(uint64_t* cycles)
{
__asm __volatile(
"cpuid # force all previous instruction to complete\n\t"
"rdtsc # TSC -> edx:eax \n\t"
"movl %%edx, 4(0) # store edx\n\t"
"movl %%eax, 0(0) # store eax\n\t"
: : "r"(cycles) : "eax", "ebx", "ecx", "edx");
}
However, when I try to compile this (XCode 4, using "Apple LLVM Compiler 2.1"), it results twice in the error "Unexpected token in memory operand" at the "\t" of the rdtsc resp. first movl instruction line.
I know basic assembler, but have no clue about the C inline assembler format.
Does anyone of you know what could be the issue with this code?
Thanks!
Assuming this is GCC inline assembly syntax, you're missing a % in the memory operand:
__asm __volatile(
"cpuid # force all previous instruction to complete\n\t"
"rdtsc # TSC -> edx:eax \n\t"
"movl %%edx, 4(%0) # store edx\n\t"
"movl %%eax, 0(%0) # store eax\n\t"
: : "r"(cycles) : "eax", "ebx", "ecx", "edx");