Explanation of spin_lock function from net/socket.c

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • johny10151981
    Top Contributor
    • Jan 2010
    • 1059

    Explanation of spin_lock function from net/socket.c

    Code:
    spin_lock(&net_family_lock);
    	if (net_families[ops->family])
    		err = -EEXIST;
    	else {
    		net_families[ops->family] = ops;
    		err = 0;
    	}
    	spin_unlock(&net_family_lock);
    Please look at the above piece of code (got from net/socket.c). Its about spin_lock function. I tried to find the explanation. But its not that easy.

    and here what I got a possible function definition of spin_lock and spin_unlock function got from wikipedia.
    Code:
    # Intel syntax
     
    lock:                        # The lock variable. 1 = locked, 0 = unlocked.
         dd      0
     
    spin_lock:
         mov     eax, 1          # Set the EAX register to 1.
     
    loop:
         xchg    eax, [lock]     # Atomically swap the EAX register with
                                 #  the lock variable.
                                 # This will always store 1 to the lock, leaving
                                 #  previous value in the EAX register.
     
         test    eax, eax        # Test EAX with itself. Among other things, this will
                                 #  set the processor's Zero Flag if EAX is 0.
                                 # If EAX is 0, then the lock was unlocked and
                                 #  we just locked it.
                                 # Otherwise, EAX is 1 and we didn't acquire the lock.
     
         jnz     loop            # Jump back to the XCHG instruction if the Zero Flag is
                                 #  not set, the lock was locked, and we need to spin.
     
         ret                     # The lock has been acquired, return to the calling
                                 #  function.
     
    spin_unlock:
         mov     eax, 0          # Set the EAX register to 0.
     
         xchg    eax, [lock]     # Atomically swap the EAX register with
                                 #  the lock variable.
     
         ret                     # The lock has been released.
    I have tried to understand the situation. How it is working.

    in the first code when we call spin_lock it enters in a loop that which will continue until ZF is set to high. In that case how program will continue to next statements? If it dont get to next statements it wont get spin_unlock.

    Or what I am missing here?
  • donbock
    Recognized Expert Top Contributor
    • Mar 2008
    • 2427

    #2
    The use of locks implies there are, or could be, multiple independent threads of execution. In this particular case, the lock is used to insure that only one thread at a time accesses the net_families array.
    1. The lock variable is initially 0.
    2. Thread 1 enters the code snippet.
    3. The lock variable is 0, so spin_lock returns immediately to Thread 1 after setting the lock variable to 1. Thread 1 enters the protected region.
    4. Thread 2 enters this same code snippet.
    5. The lock variable is 1, so spin_lock does not return to Thread 2.
    6. Thread 1 continues through the protected region. (Thread 2 is still stuck in spin_lock.)
    7. Thread 1 exits the protected region and calls spin_unlock. (Thread 2 is still stuck in spin_lock.)
    8. spin_unlock sets the lock variable to 0 from Thread 1.
    9. The next loop of spin_lock in Thread 2 finds the lock variable is 0, so it sets it back to 1 and returns.
    10. Thread 2 enters the protected region.
    11. Thread 2 exits the protected region and calls spin_unlock.
    12. spin_unlock sets the lock variable to 0 from Thread 2.

    Notice that this spin lock only works if the operating system supports preemptive multitasking. Priority scheduling could result in a deadlock. That shouldn't be possible with round-robin scheduling. I encourage you to look up the italicized terms to find out why I made these statements.

    Comment

    • johny10151981
      Top Contributor
      • Jan 2010
      • 1059

      #3
      I liked the explanation.
      But it confused my knowledge.

      It seems I need a little bit more knowledge on x86 Assembly.

      Thanks for your thoughtful answer.

      Comment

      • donbock
        Recognized Expert Top Contributor
        • Mar 2008
        • 2427

        #4
        A C version of spin_lock might look something like this:
        Code:
        volatile int lock = 0;
        void spin_lock(void) {
           while(lock != 0)
               ;   /*sit and spin*/
            lock = 1;
        }
        Consider what might happen with this version of spin_lock in the previous Thread1/Thread2 example. Suppose execution is transferred from Thread1 to Thread2 after spin_lock detects that lock is zero, but before it has a chance to set the lock to 1. Thread2 runs its copy of spin_lock, who also finds the lock to be zero. Thread2 sets the lock and enters the protected region; but while there execution returns to Thread1, who finishes exiting spin_lock and enters the protected region. We end up with both threads in the protected region at the same time -- spin_lock did not do its job.

        That's why it is so vital for lock functions to implement the lock-test and lock-set operations in an atomic, or non-interruptible / non-preemptible manner. Standard C does not provide an atomic test-and-set capability -- that's why spin_lock is written in assembly code. (It certainly isn't worth any effort to make this function run as fast as possible.)

        Look at the original spin_lock listing. Line 10 atomically exchanges the contents of the EAX register with the lock variable, but line 7 had previously set EAX to "1". So as you enter line 15, the lock variable is "1" and EAX contains the old value of the lock variable. Lines 15 and 21 cause the subroutine to loop back if the old value of the lock variable is nonzero, but if the old value is zero the subroutine falls through to line 24 and returns.

        Comment

        • johny10151981
          Top Contributor
          • Jan 2010
          • 1059

          #5
          In College I have learned 8086 Assembly language(turbo assembler). That is why a I was little confused.
          Thanks for your reply.

          This is a very good solution.

          But the point that I am missing is this is a non interrupt able routine. Say a dumb programmer create a thread that that receive data from some source and which is busy. as example

          Code:
          spin_lock(&lock_var);
           
          read_from_rs232(&variable);
          
          spin_unlock(&lock_var);
          and say for some reason read_from_rs232 is stuck. in that case would processor get stuck forever(I don't have knowledge about it). It seems like a dead lock.

          Comment

          • donbock
            Recognized Expert Top Contributor
            • Mar 2008
            • 2427

            #6
            The possibility of deadlock is present any time you have locks. It is the job of the system architect to decide what resources need to be locked and conventions for how the locks are set and released. This job can be done well, or it can be done poorly.

            A general principle is to keep the duration of a lock as short as possible.

            Notice that spin locks do not allow a thread to do anything useful while it is waiting for a lock. An alternative is a lock function that returns a success or failure indication. The calling function can then choose to do something else until the lock function returns success.

            Locks are really only needed to coordinate access to shared resources. Less sharing (more independence) between threads means fewer locks. Fewer locks means less brainpower need be expended on preventing deadlock.

            Comment

            Working...