title: Understanding Linux Kernel Circular Buffer
body: <p>There is an article at: <a href="http://lwn.net/Articles/378262/" rel="noreferrer">http://lwn.net/Articles/378262/</a> that describes the Linux kernels circular buffer implementation. I have some questions:</p>
  
  <p>Here is the "producer":</p>
  
  <pre><code>spin_lock(&amp;producer_lock);
  
  unsigned long head = buffer-&gt;head;
  unsigned long tail = ACCESS_ONCE(buffer-&gt;tail);
  
  if (CIRC_SPACE(head, tail, buffer-&gt;size) &gt;= 1) {
      /* insert one item into the buffer */
      struct item *item = buffer[head];
  
      produce_item(item);
  
      smp_wmb(); /* commit the item before incrementing the head */
  
      buffer-&gt;head = (head + 1) &amp; (buffer-&gt;size - 1);
  
      /* wake_up() will make sure that the head is committed before
       * waking anyone up */
      wake_up(consumer);
  }
  
  spin_unlock(&amp;producer_lock);
  </code></pre>
  
  <p>Questions:</p>
  
  <ol>
  <li>Since this code explicitly deals with memory ordering and atomicity what is the point of the spin_lock()?</li>
  <li>So far, my understanding is that ACCESS_ONCE stops compiler reordering, true?</li>
  <li>Does produce_item(item) simply issue all of the writes associated with the item?</li>
  <li>I believe smp_wmb() guarantees that all of the writes in produce_item(item) complete BEFORE the "publishing" write that follows it. true?</li>
  <li>The commentary on the page where I got this code seems to imply that a smp_wmb() would
  normally be needed after updating the head index, but wake_up(consumer) does this, so its not necessary. Is that true? If so why?</li>
  </ol>
  
  <p>Here is the "consumer":</p>
  
  <pre><code>spin_lock(&amp;consumer_lock);
  
  unsigned long head = ACCESS_ONCE(buffer-&gt;head);
  unsigned long tail = buffer-&gt;tail;
  
  if (CIRC_CNT(head, tail, buffer-&gt;size) &gt;= 1) {
      /* read index before reading contents at that index */
      smp_read_barrier_depends();
  
      /* extract one item from the buffer */
      struct item *item = buffer[tail];
  
      consume_item(item);
  
      smp_mb(); /* finish reading descriptor before incrementing tail */
  
      buffer-&gt;tail = (tail + 1) &amp; (buffer-&gt;size - 1);
  }
  
  spin_unlock(&amp;consumer_lock);
  </code></pre>
  
  <p>Questions specific to "consumer":</p>
  
  <ol>
  <li>What does smp_read_barrier_depends() do? From some comments in a forum it seems like you could have issued an smp_rmb() here, but on some architectures this is unnecessary (x86) and too expensive, so smp_read_barrier_depends() was created to do this optionally... That said, I don't really understand why smp_rmb() is ever necessary!</li>
  <li>Is the smp_mb() there to guarantee that all of the reads before it complete before the write after it?</li>
  </ol>