1

I have a question about mutex implementation in Linux kernel on an ARM platform.

__mutex_fastpath_lock(atomic_t *count, void (*fail_fn)(atomic_t *))
{
    int __ex_flag, __res;

    __asm__ (

             "ldrex  %0, [%2]        \n\t"
             "sub    %0, %0, #1      \n\t"
             "strex  %1, %0, [%2]    "  //for ARMv6+ ,kernel use ldrex/strex implement mutex lock.

              : "=&r" (__res), "=&r" (__ex_flag)
              : "r" (&(count)->counter)
              : "cc","memory" );

             __res |= __ex_flag;    //How can we know the "strex" operation is successfully finished or not? 
                                    //The status of (atomic_t *count) is different in these two cases. 
                                    //I wonder this is a bug ,or I did not understand the lock mechanism so well.

      if (unlikely(__res != 0))
           fail_fn(count);
      }

Thank you very much for your suggestion or answer to this question. Anything will be appreciated.

For more information about the source code, please refer to ; http://lxr.oss.org.cn/source/arch/arm/include/asm/mutex.h?v=3.5.2;a=arm

The file path is:

linux-3.5.2/arch/arm/include/asm/mutex.h
jenzz
  • 7,239
  • 6
  • 49
  • 69
Dongguo
  • 379
  • 2
  • 4
  • 14

1 Answers1

4

__res is zero if successful

STREX{cond} Rd, Rm, [Rn] Store to address in Rn and flag if successful in Rd (Rd = 0 if successful)

Note that __res is or'd with __ex_flag

 __res |= __ex_flag;

So if either of the ldrex or strex operations has failed the check fails.

Note: if the value was accessed after ldrex the exclusive access fails; strex fails and the value will not be stored. There's more information about this in the info center:

The STREX instruction performs a conditional store of a word to memory. If the exclusive monitor(s) permit the store, the operation updates the memory location and returns the value 0 in the destination register, indicating that the operation succeeded. If the exclusive monitor(s) do not permit the store, the operation does not update the memory location and returns the value 1 in the destination register.

The point is that if the exclusive access fails, either on load or store the kernel will know about it because __ex_flags=1 or __res=1, if it doesn't fail, but the mutex was already locked we will still know about it because __res=0xFFFFFFFF at that point it doesn't matter if the exclusive access has failed or not because the mutex was locked.

Now, the only problem I see is that it will store 0xFFFFFFFF into count, but that will probably be incremented again when whoever locked the mutex unlocks it, which means it could be a way for more than one thread to wait on the mutex. This is from the comments in your link:

If once decremented it isn't zero, or if its store-back fails due to a dispute on the exclusive store, we simply bail out immediately through the slow path where the lock will be reattempted until it succeeds.

iabdalkader
  • 17,009
  • 4
  • 47
  • 74
  • Thank you for your reply. But ,you see the problem ,kernel does not check %1(__ex_flag) , so we do not know whether the result is stored or not. – Dongguo Nov 12 '12 at 10:35
  • @user1296994 it does because it's or'd with `__res` so if either of them is true the operation failed. – iabdalkader Nov 12 '12 at 10:38
  • The difference is ; 1. if strex is successful, lock is decreased by 1. 2. if not ,lock is not decreased by 1. so the result is different. – Dongguo Nov 12 '12 at 10:40
  • @user1296994 ah I see what you mean, but no it's not stored if `strex` fails – iabdalkader Nov 12 '12 at 10:45
  • Consider this two cases; If %0 is not zero in ("sub %0, %0, #1 \n\t") Case 1: strex succeeds, so the lock_counter is decreased by 1. Case 2: strex fails, the lock_counter is not decreased. The results are different ,but kernel do not know that .I wonder how kernel can handle these two different cases. – Dongguo Nov 12 '12 at 10:51
  • @user1296994 kernel doesn't handle that, the exclusive access monitor *hardware* flags an error. – iabdalkader Nov 12 '12 at 10:53
  • I am sorry , I might make you confused. I know ARM will handle strex operation and it's result. What I mean is ,linux kernel does not know ,whether the lock_count is decreased or not. – Dongguo Nov 12 '12 at 10:57
  • @user1296994 yes it does, I already answered that in the first part, if the *exclusive* access fails, `__res` will be set to one, that's all the kernel needs to know, and the value is not written because the hardware takes care of that. – iabdalkader Nov 12 '12 at 11:01
  • I think I have not made myself clear. If the result __res is 1, kernel do not know whether strex fails or succeeds. because ,__res is result of (__res OR __ex_flag). There is another case: "sub %0, %0, #1 \n\t" is not zero. – Dongguo Nov 12 '12 at 11:09
  • @user1296994 if either `ldrex` or `strex` fails, the whole exclusive access has failed it doesn't matter which one has failed, they both have to succeed. if that's not clear, sorry don't know how to explain it better. – iabdalkader Nov 12 '12 at 11:16
  • For example; "ldrex %0, [%2] \n\t" //if [%2] is 0 "sub %0, %0, #1 \n\t" //so ,%0 is 0xFFFF_FFFF ,right? __res is 0xFFFF_FFFF "strex %1, %0, [%2] " //But if strex fails ,the [%2] is still zero, if it succeeds ,[%2] is 0xFFFF_FFFF, the results is different. __res |= __ex_flag; //whether is operation fails or succeeds.__res is not zero. if (unlikely(__res != 0)) //..... – Dongguo Nov 12 '12 at 11:26
  • @user1296994 it's not supposed to succeed if the count is `0` :) the count is probably either `1` == unlocked `0` == locked – iabdalkader Nov 12 '12 at 11:52
  • yes, you are right, if count is 0, %0 is 0xffff_ffff ,lock is supposed to be failed , (__res != 0) will always be true. But the point is ,we can not know ,whether strex is failed or not, so we do not know whether 0xffff_ffff is stored into count or not. am I right ,i am confused. – Dongguo Nov 12 '12 at 12:05
  • @user1296994 in this case `__ex_flag` ==1 and that's why it's or'd with `__res` – iabdalkader Nov 12 '12 at 12:08
  • in this case __ex_flag ==1 ==>I can not understand , why __ex_flags == 1 ,do you mean strex would fail ? Why can not strex succeed? – Dongguo Nov 12 '12 at 12:11
  • @user1296994 sorry, forgot that last comment, actually `__ex_flag=0`, but the point is if it fails we will know about it `__ex_flags=1`, if it doesn't but the mutex was locked we will also know about it `__res=0xFFFFFFF` the only problem I see is that it will store that into count, but maybe that's cleared somewhere else. – iabdalkader Nov 12 '12 at 12:20
  • But kernel does not care "__ex_flag" ,and branch to fail_fn(count); directly. so I am confused. I will try to find out, If I get the answer to this question ,I will leave notes to you. I am sorry to waste you so much time ,my english is not very good. Thank you very much for you reply. Have a pleasant evening.:0 – Dongguo Nov 12 '12 at 12:27
  • @user1296994 yes because that function keeps attempting to lock the mutex, check the update – iabdalkader Nov 12 '12 at 12:31
  • Now, the only problem I see is that it will store 0xFFFFFFFF into count ==>Reply: Thank you for you patient, mux .I But,do not see that. Because ,the store operation might fail. So we do not know the status of lock_count. and moreover, kernel does not check it later, kernel only know ,lock operation fails; but kernel do not know whether the memory of lock_count is changed or not.I do not know why. – Dongguo Nov 13 '12 at 01:17
  • @user1296994 read the comment on the function, in the link in your questions, or the last quote in my answer – iabdalkader Nov 13 '12 at 06:20
  • Thanks a lot . I think I got it from your reply ------------------Now, the only problem I see is that it will store 0xFFFFFFFF into count, but that will probably be incremented again when whoever locked the mutex unlocks it, which means it could be a way for more than one thread to wait on the mutex. This is from the comments in your link: -------------------------- Thank you very much. – Dongguo Nov 13 '12 at 08:18