<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Locking on Roberto Selbach</title><link>https://rselbach.com/tags/locking/</link><description>Recent content in Locking on Roberto Selbach</description><generator>Hugo</generator><language>en-US</language><lastBuildDate>Fri, 26 May 2006 20:17:00 +0000</lastBuildDate><atom:link href="https://rselbach.com/tags/locking/index.xml" rel="self" type="application/rss+xml"/><item><title>Lock Before Looking</title><link>https://rselbach.com/lock-before-looking/</link><pubDate>Fri, 26 May 2006 20:17:00 +0000</pubDate><guid>https://rselbach.com/lock-before-looking/</guid><description>&lt;p&gt;I reviewed a kernel path today that checked a pointer, acquired a spinlock, then used the pointer. It looked economical. It was also wrong.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (device&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;queue) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;spin_lock&lt;/span&gt;(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;device&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;lock);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;flush_queue&lt;/span&gt;(device&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;queue);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;spin_unlock&lt;/span&gt;(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;device&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;lock);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Another CPU can clear and free &lt;code&gt;queue&lt;/code&gt; between the check and the lock. The check does not reserve anything; it merely records a fact that may expire immediately.&lt;/p&gt;
&lt;p&gt;The simple repair is to protect both observation and use with the same lock:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;spin_lock&lt;/span&gt;(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;device&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;lock);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (device&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;queue)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;flush_queue&lt;/span&gt;(device&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;queue);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;spin_unlock&lt;/span&gt;(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;device&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;lock);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is safe only if &lt;code&gt;flush_queue&lt;/code&gt; cannot sleep and does not acquire locks in an incompatible order. Spinlocks disable preemption in the relevant context; they are not permission to perform leisurely work. If flushing can block, I need a different design, perhaps detach the queue under the spinlock and process it later with appropriate lifetime ownership.&lt;/p&gt;
&lt;p&gt;I prefer establishing ownership under one clearly documented lock rather than scattering &amp;ldquo;probably still valid&amp;rdquo; checks. The caveat is contention: a correct giant critical section can turn several processors into an expensive single processor.&lt;/p&gt;
&lt;p&gt;The lesson is not &amp;ldquo;add locks.&amp;rdquo; It is &amp;ldquo;name the invariant, then guard every transition that can violate it.&amp;rdquo; Locks without an invariant are just punctuation with cache-line traffic.&lt;/p&gt;</description></item></channel></rss>