<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Kernel on Roberto Selbach</title><link>https://rselbach.com/tags/kernel/</link><description>Recent content in Kernel on Roberto Selbach</description><generator>Hugo</generator><language>en-US</language><lastBuildDate>Tue, 09 Nov 2010 20:39:00 +0000</lastBuildDate><atom:link href="https://rselbach.com/tags/kernel/index.xml" rel="self" type="application/rss+xml"/><item><title>Counting Bits Like the Kernel</title><link>https://rselbach.com/counting-bits-like-the-kernel/</link><pubDate>Tue, 09 Nov 2010 20:39:00 +0000</pubDate><guid>https://rselbach.com/counting-bits-like-the-kernel/</guid><description>&lt;p&gt;Kernel code contains algorithms that look unnecessarily peculiar until the constraints are visible. Population count, the number of set bits in a machine word, is a good example.&lt;/p&gt;
&lt;p&gt;The obvious C version examines every bit:&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;unsigned&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;count_bits&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;unsigned&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;long&lt;/span&gt; word)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;unsigned&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; n &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; (word) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; n &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; word &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; word &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; n;
&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;This performs one iteration for every bit position up to the highest set bit. A sparse bitmap still pays for all the zeros in between.&lt;/p&gt;
&lt;p&gt;Brian Kernighan&amp;rsquo;s familiar operation removes the lowest set bit at each iteration:&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;unsigned&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;count_bits&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;unsigned&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;long&lt;/span&gt; word)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;unsigned&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; n &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; (word) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; word &lt;span style="color:#f92672"&gt;&amp;amp;=&lt;/span&gt; word &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; n&lt;span style="color:#f92672"&gt;++&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; n;
&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;Subtracting one flips the lowest set bit to zero and turns lower zeros into ones. The &lt;code&gt;&amp;amp;&lt;/code&gt; keeps everything above that bit and clears the changed suffix. The loop therefore runs once per set bit.&lt;/p&gt;
&lt;p&gt;That is excellent for sparse words and less impressive for dense ones. Kernels also use table lookups and architecture-specific instructions where available. A byte table has predictable work but adds memory access; a processor instruction can win decisively but needs dispatch or compile-time selection. There is no universally fastest source fragment independent of data and machine.&lt;/p&gt;
&lt;p&gt;Word width matters too. Kernel types and helpers make that choice explicit, while a casual userspace benchmark can accidentally compare different widths and announce a victory produced by less work.&lt;/p&gt;
&lt;p&gt;I benchmarked the two loops over generated sparse and dense arrays, keeping generation outside the timed section. As expected, the clear-lowest-bit version dominated sparse input. Dense input narrowed the gap considerably. Repeating one constant word produced lovely numbers and measured the compiler more than the routine, so I stopped doing that.&lt;/p&gt;
&lt;p&gt;The broader lesson from kernel work is to understand the representation before admiring the trick. Bitmaps pack state compactly and permit word-at-a-time operations, but contention, cache placement, and scan direction can matter more than shaving an instruction from population count.&lt;/p&gt;
&lt;p&gt;I like this algorithm because its mechanism fits in one sentence and its performance caveat requires another. Most optimization stories should have both sentences. The ones with only the first usually end in a benchmark that accidentally proves the author&amp;rsquo;s laptop exists.&lt;/p&gt;</description></item><item><title>perf Turns Counters Into Questions</title><link>https://rselbach.com/perf-turns-counters-into-questions/</link><pubDate>Tue, 08 Dec 2009 18:53:00 +0000</pubDate><guid>https://rselbach.com/perf-turns-counters-into-questions/</guid><description>&lt;p&gt;I had two versions of a parser. One finished faster, so I declared victory and nearly deleted the slower one. Before doing that, I tried the new perf tools included with recent kernels and discovered that my explanation for the improvement was wrong.&lt;/p&gt;
&lt;p&gt;The kernel&amp;rsquo;s performance-counter infrastructure provides a common way to measure hardware and software events. The &lt;code&gt;perf&lt;/code&gt; tool can count events for a command, sample execution and report where those samples landed. Instead of beginning with a profiler tied to one processor model, I can ask through one kernel interface and use the events available on this machine.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;perf stat&lt;/code&gt; is a useful first question. It reports elapsed time along with counts such as cycles, instructions, context switches and page faults. Ratios matter more than isolated totals. Instructions per cycle can suggest whether the processor is retiring useful work efficiently, while cache-related events may explain why an apparently smaller algorithm still stalls.&lt;/p&gt;
&lt;p&gt;My faster parser did not execute dramatically fewer instructions. It incurred fewer cache misses because its data was laid out more compactly. I had credited a clever branch change that happened nearby. The benchmark result was real; my story about it was fan fiction.&lt;/p&gt;
&lt;p&gt;Sampling answers a different question. &lt;code&gt;perf record&lt;/code&gt; periodically captures the current instruction pointer, and &lt;code&gt;perf report&lt;/code&gt; aggregates samples by symbol. With suitable symbols, hot functions become visible without instrumenting every call. Sampling has overhead and statistical uncertainty, but it is often much less disruptive than logging entry and exit around a hot path.&lt;/p&gt;
&lt;p&gt;Counters are constrained resources. The processor can measure only a limited number simultaneously, so events may be multiplexed. Some events are model-specific, and virtualized or restricted environments may expose less. A cache-miss count without knowing which cache or how the event is defined is an attractive number with an uncertain biography.&lt;/p&gt;
&lt;p&gt;I also avoid optimizing from one profile. Workload, input size, compiler options and machine state all matter. I run repeated measurements, preserve the input and compare complete behavior. A ten-percent gain in a microbenchmark is not useful if the changed layout doubles memory for the real service.&lt;/p&gt;
&lt;p&gt;I strongly prefer starting performance work with &lt;code&gt;perf stat&lt;/code&gt;, then sampling when the totals suggest a question. ftrace remains better for many scheduling and kernel-flow investigations; perf is especially convenient for connecting program hotspots to processor and kernel counters. Neither tool replaces understanding, but both are considerably more reliable than staring at source until one loop begins to look guilty.&lt;/p&gt;</description></item><item><title>KMS Moves the Screen Into the Kernel</title><link>https://rselbach.com/kms-moves-the-screen-into-the-kernel/</link><pubDate>Fri, 17 Jul 2009 17:34:00 +0000</pubDate><guid>https://rselbach.com/kms-moves-the-screen-into-the-kernel/</guid><description>&lt;p&gt;My test laptop changes display modes several times while booting: firmware text, a console mode, the X server&amp;rsquo;s preferred resolution, and sometimes another mode after resume. Each transition blanks the screen long enough to make me wonder whether the machine is considering a different profession.&lt;/p&gt;
&lt;p&gt;Kernel mode setting moves responsibility for configuring display modes into the kernel graphics driver. Traditionally the X server performed that setup from user space. With KMS, the kernel knows about connectors, CRTCs, encoders, framebuffers and modes early enough to establish the display before X starts.&lt;/p&gt;
&lt;p&gt;That early ownership makes boot transitions smoother, but the architectural benefit is larger. Suspend and resume already require kernel coordination with devices. If display state is also managed there, restoration does not depend on a particular user-space server reconstructing everything after the fact. Panic and virtual-terminal paths can retain a usable framebuffer as well.&lt;/p&gt;
&lt;p&gt;The Direct Rendering Manager interface exposes the resources to user space. A display server can discover connectors and available modes, choose how framebuffers map to scanout hardware, and request changes through a common kernel boundary. The kernel arbitrates access to the device rather than allowing unrelated programs to reprogram it optimistically.&lt;/p&gt;
&lt;p&gt;KMS also fits the broader memory-management work in graphics drivers. Rendering buffers and scanout buffers share hardware constraints, and transitions between them need synchronization. Putting mode setting beside DRM&amp;rsquo;s device and buffer management reduces the number of components that must agree through private arrangements.&lt;/p&gt;
&lt;p&gt;My Intel machine with a recent kernel now reaches its native mode earlier and switches to X with less flashing. Resume has also become more predictable. This is not universal evidence. Driver support varies, hardware contains creative surprises, and a failed early mode set can replace a merely ugly boot with a completely dark one.&lt;/p&gt;
&lt;p&gt;Debugging changes too. Problems that once belonged entirely to the X log may now begin in kernel messages and driver parameters. Distributions need rescue paths and conservative defaults while support matures. The mechanism is better placed, but moving code into the kernel also raises the cost of mistakes.&lt;/p&gt;
&lt;p&gt;I strongly prefer KMS as the long-term division of responsibility. Display hardware is a shared device whose state matters before, during and after the graphical session; the kernel is the natural owner. For current machines I enable it where the driver is known to work and keep a boot option that disables it. Architectural conviction is comforting, but a visible console is more immediately useful.&lt;/p&gt;</description></item><item><title>ftrace Before More printk</title><link>https://rselbach.com/ftrace-before-more-printk/</link><pubDate>Fri, 26 Jun 2009 20:51:00 +0000</pubDate><guid>https://rselbach.com/ftrace-before-more-printk/</guid><description>&lt;p&gt;I was chasing a latency spike in a kernel path and began adding &lt;code&gt;printk()&lt;/code&gt; calls. This is the kernel equivalent of looking for a gas leak with a candle: familiar, illuminating and likely to influence the situation under investigation.&lt;/p&gt;
&lt;p&gt;Recent kernels include ftrace, with controls under debugfs, normally in &lt;code&gt;/sys/kernel/debug/tracing&lt;/code&gt;. It started as a function tracer and has grown useful tracing modes and events. The useful bit is that tracing can often be selected and controlled at runtime rather than compiled into a private forest of diagnostic messages.&lt;/p&gt;
&lt;p&gt;The function tracer records kernel function activity in per-CPU ring buffers. Filters can restrict collection to relevant functions, which matters because tracing everything creates enormous output and changes timing. The function-graph tracer adds entry and return relationships, making nested execution and duration easier to see.&lt;/p&gt;
&lt;p&gt;For my problem, the scheduler tracing facilities were more revealing than raw function calls. They showed when the task stopped running, which task replaced it and when it returned. The delay I had blamed on one function was mostly time spent waiting to be scheduled. A timestamp around the function could show elapsed time, but not explain where that time went.&lt;/p&gt;
&lt;p&gt;Ring buffers are a sensible mechanism for this job. Each CPU can record events with less cross-CPU contention, and old entries can be overwritten when configured as a rolling trace. I can leave tracing ready, reproduce the spike and stop collection soon afterward. The final seconds are usually more valuable than a complete account of the machine booting and becoming bored.&lt;/p&gt;
&lt;p&gt;Tracing still has overhead. Function tracing a hot path produces work, filters cost something, and reading the trace while collecting it can perturb results. I begin with the narrowest event set that can answer the question and compare behavior with tracing disabled. A trace is evidence gathered by an instrument, not the unobserved universe itself.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;printk()&lt;/code&gt; remains appropriate for durable diagnostics that users or administrators need. It is poor as an ad hoc high-volume performance recorder: messages contend for shared facilities, flood logs and require source edits to move the probe. Temporary tracing and permanent logging solve different problems.&lt;/p&gt;
&lt;p&gt;For kernel execution and scheduling questions, I try ftrace first. A carefully placed message can fill in semantic information the trace lacks. This is faster and leaves less debris in the source, including my traditional final patch removing forty-seven increasingly desperate print statements.&lt;/p&gt;</description></item><item><title>Cgroups Are Accounting Before Containment</title><link>https://rselbach.com/cgroups-are-accounting-before-containment/</link><pubDate>Thu, 22 Jan 2009 20:16:00 +0000</pubDate><guid>https://rselbach.com/cgroups-are-accounting-before-containment/</guid><description>&lt;p&gt;One of two batch jobs was eating the workstation, but their worker processes came and went too quickly for &lt;code&gt;top&lt;/code&gt; to give me a useful total. Watching individual PIDs was the wrong level. I wanted the CPU bill for each job and all of its children.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;cpuacct&lt;/code&gt; cgroup controller does that accounting. I put each launcher in its own group before it forked workers. The children inherited the group, so short-lived compiler and helper processes charged CPU time to the same job without a polling script trying to catch them.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cpuacct.usage&lt;/code&gt; gives accumulated CPU time in nanoseconds. Reading it before and after a run gave me a simple total for the whole process tree. &lt;code&gt;cpuacct.stat&lt;/code&gt; split the charge into user and system time in clock ticks, which showed that the troublesome job was spending most of its time in user code rather than hammering the kernel.&lt;/p&gt;
&lt;p&gt;The counters are cumulative, so I record a starting value or create a fresh group for a run. A single reading is not a percentage. Dividing the delta by elapsed wall time gives a rough average, and a multithreaded job can legitimately consume more than one CPU-second per second on a multicore machine.&lt;/p&gt;
&lt;p&gt;This also avoids a trap in process snapshots. If a parent exits after launching workers, summing only its current descendants misses work already completed. The group&amp;rsquo;s counter keeps the charge after those tasks are gone.&lt;/p&gt;
&lt;p&gt;Accounting did not slow either job or reserve a processor. That was fine. It showed that the job I had blamed was innocent and the other one used nearly three times the CPU I expected. Only then did scheduler policy seem worth discussing.&lt;/p&gt;
&lt;p&gt;For now I am leaving the groups as meters. A reliable number is already an improvement over glaring at whichever PID happens to be at the top of &lt;code&gt;top&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Idle Host, Busy Guest</title><link>https://rselbach.com/idle-host-busy-guest/</link><pubDate>Wed, 25 Jun 2008 20:45:00 +0000</pubDate><guid>https://rselbach.com/idle-host-busy-guest/</guid><description>&lt;p&gt;My laptop stayed warm with an apparently idle KVM guest running. The guest reported nearly no processor use, and the host showed only small bursts, so I first blamed inaccurate temperature sensors. Sensors are convenient suspects because they cannot write rebuttals.&lt;/p&gt;
&lt;p&gt;I stopped services in the guest one by one. A periodic timer user was waking it frequently, but even a minimal guest produced more activity than expected. Virtual timer interrupts, emulated devices and host-side handling all require exits from guest execution. An idle guest is therefore not necessarily an idle host.&lt;/p&gt;
&lt;p&gt;Tickless operation helps when the guest kernel can avoid unnecessary periodic ticks and when KVM presents timers accurately enough for longer idle periods. It cannot eliminate device emulation or a program requesting frequent wakeups. The host must also be tickless while idle; saving ticks in one layer and generating them in the other is elaborate bookkeeping, not power management.&lt;/p&gt;
&lt;p&gt;After using a tickless guest kernel and disabling a polling monitor, host wakeups dropped and the machine cooled. Timekeeping remained the thing to watch: aggressive experiments are worthless if the guest clock drifts or timers fire late.&lt;/p&gt;
&lt;p&gt;My conclusion is to shut down unused guests on battery and to measure host wakeups, not merely guest CPU percentage. For a guest that must remain available, remove polling and use appropriate virtual devices where supported. Virtualization can make a machine appear to be doing nothing in two operating systems simultaneously while the processor is, with admirable professionalism, doing work for both.&lt;/p&gt;</description></item><item><title>Trying Control Groups After 2.6.24</title><link>https://rselbach.com/cgroups-after-2624/</link><pubDate>Fri, 04 Apr 2008 19:15:00 +0000</pubDate><guid>https://rselbach.com/cgroups-after-2624/</guid><description>&lt;p&gt;I wanted a compile job and its helper processes to stop overwhelming everything else on my test machine. Renicing the shell helped until the build launched processes with behavior I had not accounted for, and it did nothing about memory consumption.&lt;/p&gt;
&lt;p&gt;Linux 2.6.24 includes the control groups framework, so I enabled the relevant options and mounted a control-group filesystem for testing. My first attempt was to write the compiler&amp;rsquo;s process ID into a group after the build had started. Some children were already elsewhere, and the result was inconsistent enough to look supernatural.&lt;/p&gt;
&lt;p&gt;A control group is a hierarchy used to classify tasks. Controllers attach particular resource policies or accounting to that hierarchy. The framework itself is not one universal &amp;ldquo;make this process small&amp;rdquo; knob. CPU scheduling, processor sets and other controllers expose different files and semantics. A task belongs to a group in a hierarchy, and children normally inherit membership, so classification should happen before launching the workload.&lt;/p&gt;
&lt;p&gt;I created a group, moved my test shell into it, and then started the build from that shell. Its descendants appeared in the same group. Applying CPU scheduling controls made the machine more responsive under contention without changing every process individually. Memory control is a separate matter and depends on kernel configuration; enabling it also has overhead that should not be waved away.&lt;/p&gt;
&lt;p&gt;The hierarchy is powerful but easy to design badly. Groups may represent users, services or workloads, and those choices do not always nest neatly. Multiple hierarchies can attach different controllers, which provides flexibility at the cost of another opportunity to create an administrative puzzle.&lt;/p&gt;
&lt;p&gt;For now I regard cgroups as kernel machinery for building resource-management policy, not as a finished desktop feature. The interface is low-level and sharp-edged. Tools will need to create groups, place tasks early, apply limits coherently and clean up afterward.&lt;/p&gt;
&lt;p&gt;My practical conclusion is to start with one measured problem and one controller. Verify membership before interpreting results, and do not confuse accounting with enforcement. Cgroups can organize a family of processes in a way &lt;code&gt;nice&lt;/code&gt; alone cannot. They can also organize one&amp;rsquo;s mistakes into a tidy hierarchy, which is progress of a sort.&lt;/p&gt;</description></item><item><title>CFS After the First Week</title><link>https://rselbach.com/cfs-after-the-first-week/</link><pubDate>Sun, 28 Oct 2007 17:35:00 +0000</pubDate><guid>https://rselbach.com/cfs-after-the-first-week/</guid><description>&lt;p&gt;I upgraded my workstation to Linux 2.6.23 mainly to try the Completely Fair Scheduler. The machine compiles code, plays music, runs a browser with an unreasonable number of pages, and occasionally hosts a virtual machine. Under a large parallel build, the old setup could make switching desktops feel as though the keyboard had been sent by post.&lt;/p&gt;
&lt;p&gt;My first test was not clever. I started a build, dragged windows around and declared the new scheduler smoother. Then I rebooted into the older kernel and it also seemed smooth. Human perception is a wonderful measurement instrument if the desired unit is confidence.&lt;/p&gt;
&lt;p&gt;For a better comparison, I repeated the same build with the same compiler jobs, timed it, and introduced an interactive task that periodically needed a small amount of processor time. Total build time changed little. The interesting difference was the delay before the interactive task ran when all cores were busy. Large stalls became less frequent with 2.6.23, though disk activity could still make the desktop miserable for entirely different reasons.&lt;/p&gt;
&lt;p&gt;CFS approaches scheduling without the old collection of active and expired priority arrays. It tracks each runnable task&amp;rsquo;s virtual runtime, an adjusted measure of how much processor service that task has received. The task with the smallest virtual runtime is the one most entitled to run next. Runnable tasks are kept in a red-black tree, making that leftmost choice efficient as tasks arrive, block and wake.&lt;/p&gt;
&lt;p&gt;The word &amp;ldquo;fair&amp;rdquo; needs care. Equal nice levels should receive roughly equal shares over time; it does not mean every task receives an identical slice at every instant. Nice values weight the virtual runtime, so a task with lower priority accumulates entitlement differently and receives a smaller share. Sleeper behavior emerges because a task that blocks stops consuming processor time. When it wakes, it may be behind its competitors and therefore run promptly, within limits intended to prevent absurd advantages.&lt;/p&gt;
&lt;p&gt;My naive mental model was that CFS simply gives tiny slices to interactive programs and large slices to compilers. That is not how it identifies interactivity. There is no reliable label saying &amp;ldquo;this is the music player.&amp;rdquo; Instead, the accounting rewards tasks that use little processor time and wake to perform brief work. A music player, terminal or window manager often behaves that way, but so can a less noble process.&lt;/p&gt;
&lt;p&gt;The scheduler also has to balance two competing costs. Switching too often improves apparent responsiveness but loses time to context switches and cache disruption. Waiting too long lets a runnable task monopolise a processor. CFS uses a target scheduling latency and derives slices from the number and weights of runnable tasks, subject to a minimum granularity. As the run queue grows, it cannot promise that every task runs within the same tiny interval without turning the processor into a context-switching demonstration.&lt;/p&gt;
&lt;p&gt;This explains one of my results. With a modest build load, interactive delays stayed short. With a deliberately ridiculous number of runnable jobs, latency grew again. Fair scheduling cannot manufacture processor time. It can distribute scarcity coherently, which is less exciting but more useful.&lt;/p&gt;
&lt;p&gt;Another misleading result came from I/O. During linking, the desktop paused even though CFS was selecting processor tasks correctly. The disk queue was saturated and applications blocked waiting for files. Changing the CPU scheduler did not cure that. Likewise, swapping overwhelmed every subtle scheduler difference. Before blaming CFS, I now check whether the delayed task is actually runnable rather than sleeping on storage.&lt;/p&gt;
&lt;p&gt;Nice levels behaved more predictably in my tests than before, but group fairness remains a practical concern. If one user starts one processor-bound task and another starts twenty, scheduling every task independently can grant the second user most of the machine. There is work around grouping tasks, but the default desktop case still mostly exposes per-task fairness. The name should not be read as a constitutional guarantee.&lt;/p&gt;
&lt;p&gt;I also tried changing scheduler tunables because numbers invite interference. Reducing latency made window feedback slightly sharper under synthetic load and increased context switches. Increasing it helped throughput by an amount too small to distinguish confidently while making the terminal occasionally sticky. The defaults survived my experiments, an outcome that wounded my pride but spared my configuration files.&lt;/p&gt;
&lt;p&gt;After a week, I am keeping 2.6.23. The improvement is not that compilations finish magically sooner. They do not. The improvement is that short, waking tasks more often get service without elaborate interactivity heuristics, and nice values map onto a cleaner accounting model. That makes a loaded workstation feel less capricious.&lt;/p&gt;
&lt;p&gt;The practical conclusion is to test CFS with repeatable mixed workloads, not window wiggling alone. Separate CPU delay from disk delay, compare distributions of latency rather than one lucky run, and resist tuning until a real workload demonstrates a problem. CFS is a substantial scheduler change, not a performance patch that makes every number smaller. On my machine it makes contention more orderly. Given what computers usually do under pressure, orderly is a fairly ambitious achievement.&lt;/p&gt;</description></item><item><title>Tickless Is Not Sleepless</title><link>https://rselbach.com/tickless-is-not-sleepless/</link><pubDate>Mon, 10 Sep 2007 18:45:00 +0000</pubDate><guid>https://rselbach.com/tickless-is-not-sleepless/</guid><description>&lt;p&gt;I built a recent kernel with tickless idle enabled because the laptop fan had begun running while the machine did apparently nothing. I expected the new kernel to become silent by virtue of one configuration option. It did not. The fan is apparently not impressed by release notes.&lt;/p&gt;
&lt;p&gt;My naive test was to leave a terminal open, wait a minute, and compare the battery estimate. That number wandered by nearly an hour between readings. I then watched processor usage, which stayed close to zero while the fan continued. Neither test told me what was waking the processor.&lt;/p&gt;
&lt;p&gt;The periodic timer tick normally interrupts each processor at a fixed frequency so the kernel can account for time and perform scheduling work. During idle periods, those interrupts wake an otherwise sleeping processor for very little purpose. Tickless idle programs the next timer event instead, allowing a longer uninterrupted sleep. It cannot help if an application or driver requests a timer every few milliseconds.&lt;/p&gt;
&lt;p&gt;On my machine, an application polling for status updates was doing exactly that. Closing it reduced wakeups dramatically and the fan eventually slowed. A network driver also produced regular activity when the interface was unused. Disabling the interface for a test separated that problem from the timer configuration.&lt;/p&gt;
&lt;p&gt;Measuring several idle intervals also mattered. One short sample could be dominated by mail checking, disk flushing or my own impatient mouse movement.&lt;/p&gt;
&lt;p&gt;There are limits to the name. Tickless does not mean the kernel never uses timer interrupts, and the current work mainly avoids ticks while a processor is idle. A busy processor still needs scheduling and accounting. Hardware timer support matters too; unreliable timers can turn a power improvement into missed timeouts or clocks that drift.&lt;/p&gt;
&lt;p&gt;The practical lesson is that tickless operation removes one systematic source of wakeups. It does not forgive programs that poll constantly, nor drivers that chatter with hardware for entertainment. I am keeping it enabled because idle residency improved and battery measurements over several complete runs are better. But the useful debugging question is now &amp;ldquo;who wakes the processor?&amp;rdquo; rather than &amp;ldquo;is CPU usage low?&amp;rdquo; Zero percent can be composed of thousands of tiny interruptions, which is a very computer-like definition of resting.&lt;/p&gt;</description></item><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><item><title>What the Desktop Taught Me This Year</title><link>https://rselbach.com/what-the-desktop-taught-me-this-year/</link><pubDate>Fri, 16 Dec 2005 18:15:00 +0000</pubDate><guid>https://rselbach.com/what-the-desktop-taught-me-this-year/</guid><description>&lt;p&gt;I spent much of this year fixing things that I first tried to solve at the wrong layer. A missing button event sent me into desktop settings. A wandering disk name produced a worse shell script. A driver race invited more logging. My instincts have been very energetic, if not consistently useful.&lt;/p&gt;
&lt;p&gt;The better method is to follow the mechanism. Kernel devices appear through the device model and sysfs; udev applies naming policy. Input travels from the kernel through X.Org to applications. Qt objects have explicit parent ownership. Deferred kernel work has context and lifetime rules. Git history is a graph of objects, not a remote folder with ceremony.&lt;/p&gt;
&lt;p&gt;KDE 3.5 ties the user-facing side together with unusual steadiness. Its maturity is not one clever feature but the accumulated reliability of applications, libraries, and conventions.&lt;/p&gt;
&lt;p&gt;My practical conclusion for the year is to identify the boundary before editing the configuration or code. Ask what layer owns the decision, what evidence crosses that boundary, and what lifetime the data has. Then make one change and test it.&lt;/p&gt;
&lt;p&gt;This sounds obvious when written in December. In February it apparently required an oops. Education remains committed to memorable examples.&lt;/p&gt;</description></item><item><title>Replacing the Directory Poll With inotify</title><link>https://rselbach.com/replacing-the-directory-poll-with-inotify/</link><pubDate>Fri, 16 Sep 2005 20:05:00 +0000</pubDate><guid>https://rselbach.com/replacing-the-directory-poll-with-inotify/</guid><description>&lt;p&gt;Now that Linux 2.6.13 includes inotify, I replaced a timer in a small file-watching utility. The timer scanned a directory every second, which was simple, portable, and wasteful. It also introduced the charming choice between delayed updates and more frequent useless work.&lt;/p&gt;
&lt;p&gt;My first inotify version assumed each &lt;code&gt;read()&lt;/code&gt; returned one complete event. It worked during gentle testing and became nonsense when several files changed quickly. The interface returns a byte stream containing one or more variable-length &lt;code&gt;struct inotify_event&lt;/code&gt; records. Correct code walks the buffer by each fixed header plus its &lt;code&gt;len&lt;/code&gt; field.&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;union&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; inotify_event event;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;char&lt;/span&gt; bytes[&lt;span style="color:#ae81ff"&gt;4096&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;} buffer;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;ssize_t&lt;/span&gt; count;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;size_t&lt;/span&gt; offset &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;do&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; count &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;read&lt;/span&gt;(fd, buffer.bytes, &lt;span style="color:#66d9ef"&gt;sizeof&lt;/span&gt;(buffer.bytes));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;} &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; (count &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; errno &lt;span style="color:#f92672"&gt;==&lt;/span&gt; EINTR);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&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; (count &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;perror&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;read&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;exit&lt;/span&gt;(EXIT_FAILURE);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&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; (count &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;fprintf&lt;/span&gt;(stderr, &lt;span style="color:#e6db74"&gt;&amp;#34;inotify descriptor reached EOF&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;exit&lt;/span&gt;(EXIT_FAILURE);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; (offset &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;size_t&lt;/span&gt;)count) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; inotify_event &lt;span style="color:#f92672"&gt;*&lt;/span&gt;event;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;size_t&lt;/span&gt; event_size;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&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; ((&lt;span style="color:#66d9ef"&gt;size_t&lt;/span&gt;)count &lt;span style="color:#f92672"&gt;-&lt;/span&gt; offset &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;sizeof&lt;/span&gt;(&lt;span style="color:#f92672"&gt;*&lt;/span&gt;event)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;fprintf&lt;/span&gt;(stderr, &lt;span style="color:#e6db74"&gt;&amp;#34;short inotify event header&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;exit&lt;/span&gt;(EXIT_FAILURE);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; event &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; inotify_event &lt;span style="color:#f92672"&gt;*&lt;/span&gt;)(buffer.bytes &lt;span style="color:#f92672"&gt;+&lt;/span&gt; offset);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; event_size &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;sizeof&lt;/span&gt;(&lt;span style="color:#f92672"&gt;*&lt;/span&gt;event) &lt;span style="color:#f92672"&gt;+&lt;/span&gt; event&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;len;
&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; (event_size &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;size_t&lt;/span&gt;)count &lt;span style="color:#f92672"&gt;-&lt;/span&gt; offset) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;fprintf&lt;/span&gt;(stderr, &lt;span style="color:#e6db74"&gt;&amp;#34;short inotify event name&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;exit&lt;/span&gt;(EXIT_FAILURE);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;handle_event&lt;/span&gt;(event);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; offset &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; event_size;
&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;The union gives the byte storage suitable alignment for &lt;code&gt;struct inotify_event&lt;/code&gt;; a plain character array doesn&amp;rsquo;t promise that. The code also deals with interruption, read failure, EOF, and truncated records before treating bytes as an event. A watch identifies a pathname to the kernel, and events report changes associated with a watch descriptor.&lt;/p&gt;
&lt;p&gt;The largest conceptual correction was learning that notifications are hints about state changes, not a private journal guaranteed to preserve my worldview. Events can arrive in bursts. A queue can overflow. A watched object can move or disappear. On overflow, the safe response is to rescan and rebuild known state rather than guess what was missed.&lt;/p&gt;
&lt;p&gt;Renames deserve similar care. Related move events carry a cookie that can help pair them, but the application still needs sensible behavior when only one side is visible. Watching a directory also does not automatically mean recursively watching every directory below it.&lt;/p&gt;
&lt;p&gt;I kept one function that performs a complete scan and made event handling update the same internal model. That gives the program a recovery path and keeps startup behavior consistent with overflow recovery. The event loop is an optimization over known state, not the only source of truth.&lt;/p&gt;
&lt;p&gt;Compared with polling, inotify is immediate and avoids repeated directory walks when nothing changes. It also exposes more edge cases because the kernel reports actual operations rather than letting a periodic snapshot blur them together.&lt;/p&gt;
&lt;p&gt;I prefer the new mechanism, with one reservation: event-driven doesn&amp;rsquo;t mean infallible. Read complete buffers, parse every record, expect overflow, and keep a rescan path. The kernel says something happened; deciding what the application should now believe is still our job.&lt;/p&gt;</description></item><item><title>Making a Use-After-Free Leave Tracks</title><link>https://rselbach.com/making-a-use-after-free-leave-tracks/</link><pubDate>Fri, 12 Aug 2005 17:50:00 +0000</pubDate><guid>https://rselbach.com/making-a-use-after-free-leave-tracks/</guid><description>&lt;p&gt;A driver crashed several calls after an object had been freed. The final bad pointer looked plausible, and ordinary logs only showed that teardown had happened sometime earlier. I needed the stale access to leave a clearer fingerprint.&lt;/p&gt;
&lt;p&gt;I rebuilt the test kernel with &lt;code&gt;CONFIG_DEBUG_SLAB&lt;/code&gt;. The slab allocator&amp;rsquo;s debugging checks and poisoning replace freed storage with a recognizable pattern instead of leaving yesterday&amp;rsquo;s fields looking valid. The next failure stopped looking like random corruption: the pointer led into an object full of poison bytes.&lt;/p&gt;
&lt;p&gt;That didn&amp;rsquo;t identify the owner by itself. I decoded the complete oops against the matching unstripped &lt;code&gt;vmlinux&lt;/code&gt;, then followed the call chain back to a timer callback. The timer retained the private pointer after the remove path had called &lt;code&gt;kfree()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The repair was pleasantly unoriginal: prevent rearming, delete the timer with the synchronizing form, and only then free the object. I ran repeated load, activity, and unload cycles with slab debugging still enabled. I also forced probe failures after each acquisition to make sure partially built objects followed the same lifetime rules.&lt;/p&gt;
&lt;p&gt;Poisoning is a diagnostic, not memory safety. It works when the freed bytes haven&amp;rsquo;t already been reused, and it makes a debug kernel slower. Here it turned a believable stale structure into an unmistakably dead one, which was exactly the clue I needed.&lt;/p&gt;</description></item><item><title>Watching Linux History Move to Git</title><link>https://rselbach.com/watching-linux-history-move-to-git/</link><pubDate>Fri, 22 Apr 2005 19:35:00 +0000</pubDate><guid>https://rselbach.com/watching-linux-history-move-to-git/</guid><description>&lt;p&gt;The interesting part of Git this week is no longer that it exists. Kernel developers are beginning to use it for actual Linux work, which changes the test from “can these objects be written?” to “can people exchange and trust history under pressure?”&lt;/p&gt;
&lt;p&gt;I tried to imitate the old centralized habit by treating one repository as the place where truth lived and every other copy as a temporary client. That misses Git&amp;rsquo;s shape. Each repository can hold commits and their parent relationships. Fetching obtains objects and updates selected references; merging combines lines of development rather than asking a central server to manufacture history.&lt;/p&gt;
&lt;p&gt;The kernel workflow makes the reason concrete. Maintainers can collect changes for their subsystem, preserve those commits, and pass a resulting line of history upward. The top-level maintainer can inspect and merge it. Signed e-mail and established review practices still matter; a fast object database does not decide whether a patch is wise.&lt;/p&gt;
&lt;p&gt;I also learned not to confuse content identity with human trust. A hash can reveal that an object changed. It cannot tell me whether the author understood locking or whether I fetched from the person I intended. Technical integrity and project trust reinforce one another, but they are not synonyms.&lt;/p&gt;
&lt;p&gt;Performance is already part of the appeal. Operations over the kernel tree need to be quick enough that developers use them freely. Cheap local history and branches encourage smaller experiments because recording work does not require a conversation with a remote machine.&lt;/p&gt;
&lt;p&gt;That changes behavior as much as speed. A maintainer can inspect and organize work before publishing it instead of using the shared repository as a scratch pad.&lt;/p&gt;
&lt;p&gt;The interface remains under construction, and I would not recommend that every project switch during lunch. Linux has an urgent need, unusually capable maintainers, and a willingness to work close to the machinery. That is a special environment, not a universal migration plan.&lt;/p&gt;
&lt;p&gt;But adoption by the kernel gives Git a severe and useful proving ground. If it can preserve a large, branching C codebase while maintainers exchange changes independently, the design will have earned attention. I am keeping my test repository now. Calling it comfortable would be generous; calling it merely a toy would already be unfair.&lt;/p&gt;</description></item><item><title>A 2.6 Driver and the Trail of Evidence</title><link>https://rselbach.com/a-2-6-driver-and-the-trail-of-evidence/</link><pubDate>Fri, 11 Feb 2005 20:45:00 +0000</pubDate><guid>https://rselbach.com/a-2-6-driver-and-the-trail-of-evidence/</guid><description>&lt;p&gt;I ported a small driver experiment to a newer 2.6 kernel this week. The old code compiled after a few mechanical edits, so I congratulated myself much too early. Loading the module produced an oops during device removal, which is the kernel&amp;rsquo;s way of reviewing code without softening the language.&lt;/p&gt;
&lt;p&gt;My first attempt was to stare at the final faulting line and add a null check. That stopped one crash but left the design wrong. The pointer was not unexpectedly null; it referred to an object whose lifetime had ended. A defensive condition would merely make the race less visible.&lt;/p&gt;
&lt;p&gt;The 2.6 device model asks drivers to express relationships among buses, devices, and drivers. Registration is not just bookkeeping. It establishes ownership and determines which callbacks can run as devices appear and disappear. I had treated &lt;code&gt;remove&lt;/code&gt; as the reverse spelling of &lt;code&gt;probe&lt;/code&gt;, but not as the reverse sequence of acquired resources.&lt;/p&gt;
&lt;p&gt;I wrote down each successful step in &lt;code&gt;probe&lt;/code&gt;: enable the device, reserve its region, allocate private state, initialize synchronization, and expose the interface. Then I made every failure path unwind only the steps already completed. &lt;code&gt;remove&lt;/code&gt; performs the same cleanup in reverse order after preventing new work from entering.&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;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;sample_probe&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; device &lt;span style="color:#f92672"&gt;*&lt;/span&gt;device)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; sample &lt;span style="color:#f92672"&gt;*&lt;/span&gt;sample;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; error;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sample &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;kmalloc&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;sizeof&lt;/span&gt;(&lt;span style="color:#f92672"&gt;*&lt;/span&gt;sample), GFP_KERNEL);
&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; (&lt;span style="color:#f92672"&gt;!&lt;/span&gt;sample)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;ENOMEM;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;memset&lt;/span&gt;(sample, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;sizeof&lt;/span&gt;(&lt;span style="color:#f92672"&gt;*&lt;/span&gt;sample));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; error &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;reserve_hardware&lt;/span&gt;(sample);
&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; (error)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;goto&lt;/span&gt; free_sample;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/* Published callbacks must be able to find fully initialized state. */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;dev_set_drvdata&lt;/span&gt;(device, sample);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; error &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;register_interface&lt;/span&gt;(sample);
&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; (error)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;goto&lt;/span&gt; clear_drvdata;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;clear_drvdata:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;dev_set_drvdata&lt;/span&gt;(device, NULL);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;release_hardware:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;release_hardware&lt;/span&gt;(sample);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;free_sample:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;kfree&lt;/span&gt;(sample);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; error;
&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;This is shortened code, but the order matters: private data is attached before &lt;code&gt;register_interface()&lt;/code&gt; makes callbacks possible. If publication fails, the failure path clears that pointer before releasing and freeing the state. In C, a linear chain of labels is often clearer than nested cleanup conditionals.&lt;/p&gt;
&lt;p&gt;Removal uses the other half of the same rule:&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;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;sample_remove&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; device &lt;span style="color:#f92672"&gt;*&lt;/span&gt;device)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; sample &lt;span style="color:#f92672"&gt;*&lt;/span&gt;sample &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;dev_get_drvdata&lt;/span&gt;(device);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;unregister_interface&lt;/span&gt;(sample);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;stop_hardware&lt;/span&gt;(sample);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;cancel_deferred_work&lt;/span&gt;(sample);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;dev_set_drvdata&lt;/span&gt;(device, NULL);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;release_hardware&lt;/span&gt;(sample);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;kfree&lt;/span&gt;(sample);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&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;&lt;code&gt;unregister_interface()&lt;/code&gt; first prevents new user callbacks. The hardware is then stopped and outstanding work is drained while &lt;code&gt;drvdata&lt;/code&gt; still points to live memory. Only after no callback can use it do I detach and free it.&lt;/p&gt;
&lt;p&gt;For evidence, I enabled the kernel debugging options relevant to memory and locking and built a kernel specifically for this work. They cost performance, but a development kernel is a measuring instrument, not a racing bicycle. I also kept the unstripped &lt;code&gt;vmlinux&lt;/code&gt; and the exact &lt;code&gt;.config&lt;/code&gt; beside the test build.&lt;/p&gt;
&lt;p&gt;When the next oops appeared, the symbolic trace identified both the callback and its caller. &lt;code&gt;printk()&lt;/code&gt; markers around acquisition and release confirmed that queued work could still run after teardown began. The repair was to stop new requests, cancel or drain deferred activity, and only then free the state it could reference.&lt;/p&gt;
&lt;p&gt;The order now looks like this:&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-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;block new work
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;stop device activity
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;wait for deferred work
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;remove user-visible interface
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;release hardware resources
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;free private state
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I tested failure paths too. Returning an artificial error after each acquisition showed whether cleanup restored the previous state. Module load and unload in a loop then exercised the unremarkable path repeatedly. Repetition is valuable because races dislike appointments.&lt;/p&gt;
&lt;p&gt;I included reference counts in the review even though the first crash did not mention them. A reference is a claim that an object must remain alive, not merely an integer to increment near a pointer. Every path acquiring one needs a visible release, and teardown must prevent new claims before waiting for old ones. Counting without a shutdown rule only measures how confused the lifetime has become.&lt;/p&gt;
&lt;p&gt;Lock choice followed the same reasoning. I did not replace every lock with a larger one to make the warning disappear. I listed which fields formed one invariant and which contexts accessed them. A spinlock suited the short state transition shared with interrupt context; work that could sleep stayed outside it. Narrow critical sections made both the rules and the traces easier to read.&lt;/p&gt;
&lt;p&gt;One subtle lesson was to distrust logs as timing-neutral. A &lt;code&gt;printk()&lt;/code&gt; can alter scheduling enough to hide a race. I used it to establish broad ordering, then relied on assertions, debug checks, and repeated tests rather than declaring victory when extra logging made the crash vanish.&lt;/p&gt;
&lt;p&gt;An oops after testing still required a disciplined capture. I saved the complete report, not only the final address, and matched it to the exact kernel image. A decoded call chain is context: it shows how execution arrived, which often matters more than the instruction that finally touched invalid memory.&lt;/p&gt;
&lt;p&gt;The kernel APIs will continue to evolve, and copying a driver from an older tree will continue to be tempting. The durable knowledge is not a particular field name. It is the discipline of checking the current in-tree callers, understanding callback context, defining object lifetime, and making cleanup the exact inverse of setup.&lt;/p&gt;
&lt;p&gt;I still prefer user-space debugging, where a bad pointer usually ruins one process rather than the afternoon. The kernel does leave a trail if the build preserves symbols and the code preserves invariants. That&amp;rsquo;s better than adding null checks until the machine goes quiet.&lt;/p&gt;</description></item><item><title>Sleeping in the Kernel Without Regret</title><link>https://rselbach.com/sleeping-in-the-kernel-without-regret/</link><pubDate>Fri, 28 Jan 2005 22:30:00 +0000</pubDate><guid>https://rselbach.com/sleeping-in-the-kernel-without-regret/</guid><description>&lt;p&gt;A driver experiment needed to do a little cleanup after an interrupt. I put the cleanup directly in the interrupt handler because it was only a little work. Then that little work called something that could sleep, and the kernel objected with considerably more confidence than I had shown.&lt;/p&gt;
&lt;p&gt;The naive model says an interrupt handler is just a function the kernel calls quickly. The missing detail is context. Hard interrupt context cannot sleep: there is no process there to suspend in the normal sense, and holding up interrupt handling harms the rest of the machine.&lt;/p&gt;
&lt;p&gt;The useful pattern is to split the operation. The handler acknowledges the device, records the minimal state under the right lock, and schedules deferred work. A workqueue function later runs in process context and may use operations that can sleep.&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;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;device_work&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;data)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; sample_device &lt;span style="color:#f92672"&gt;*&lt;/span&gt;dev &lt;span style="color:#f92672"&gt;=&lt;/span&gt; data;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/* Slow cleanup is safe in process context. */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;finish_request&lt;/span&gt;(dev);
&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;The exact initialization APIs deserve a check against the headers for the kernel being built; this area has changed before and surely will again. The invariant matters more than memorizing one macro: know which context executes the callback, what locks are held, and whether every function it calls may sleep.&lt;/p&gt;
&lt;p&gt;Allocation flags make the distinction concrete. Code in a context that cannot sleep must not request an allocation mode that may block. Moving slow work to a workqueue lets that callback use normal process-context operations, but it does not make shared state safe automatically.&lt;/p&gt;
&lt;p&gt;Teardown is the other half. Before freeing the device state, the driver must stop scheduling work and ensure already queued callbacks have finished. Otherwise the deferred function merely postpones the crash until after the object is gone.&lt;/p&gt;
&lt;p&gt;I added an explicit comment at the scheduling point and reduced the handler to the smallest useful unit. Latency improved, but the larger gain was conceptual. Once context is part of the function&amp;rsquo;s contract, calls that looked harmless become obviously wrong.&lt;/p&gt;
&lt;p&gt;Kernel C is not difficult because the syntax is exotic. It is difficult because an ordinary-looking call carries constraints about context, ownership, and concurrency. Ignoring those constraints produces bugs that wait patiently for the least convenient machine. Mine at least had the courtesy to complain immediately.&lt;/p&gt;</description></item><item><title>Following the Device Through sysfs</title><link>https://rselbach.com/following-the-device-through-sysfs/</link><pubDate>Fri, 19 Nov 2004 18:40:00 +0000</pubDate><guid>https://rselbach.com/following-the-device-through-sysfs/</guid><description>&lt;p&gt;A USB card reader prompted today&amp;rsquo;s trip through &lt;code&gt;/sys&lt;/code&gt;. I wanted one answer: which physical device sits behind the block-class entry &lt;code&gt;/sys/block/sda&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;The useful relationship is the &lt;code&gt;device&lt;/code&gt; symlink. &lt;code&gt;/sys/block/sda&lt;/code&gt; presents the disk as a member of the block class; its &lt;code&gt;device&lt;/code&gt; link points into the physical device hierarchy. Those are two views of one kernel object, not two devices.&lt;/p&gt;
&lt;p&gt;I started at the block entry and followed its device link rather than guessing from the node name:&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-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ readlink -f /sys/block/sda/device
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/sys/devices/pci0000:00/0000:00:10.4/usb2/2-2/2-2:1.0/host1/1:0:0:0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The exact target is machine-specific. Here it says that the block disk came through a SCSI host attached to USB interface &lt;code&gt;2-2:1.0&lt;/code&gt;, itself below a PCI USB controller. On another machine the chain will differ, but the link still joins the class view to the device that implements it.&lt;/p&gt;
&lt;p&gt;Walking upward from that target lets me inspect the parents that actually own bus attributes. The block entry doesn&amp;rsquo;t need to copy USB details into its own directory; the parent links already express the relationship.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t parse the punctuation in the resolved path. Directory names and topology are useful while diagnosing a machine, but exported links and attributes are the interface. Hard-coding every component would just trade &lt;code&gt;/dev/sda&lt;/code&gt; for a longer accidental name.&lt;/p&gt;
&lt;p&gt;This also explains why &lt;code&gt;/dev/sda&lt;/code&gt; isn&amp;rsquo;t a hardware biography. The node names a block interface; sysfs supplies the link back to the device model when I need to ask where that interface came from.&lt;/p&gt;
&lt;p&gt;The service corridors are a little dim, but this one symlink answers the question without guesswork.&lt;/p&gt;</description></item><item><title>Watching Latency on Linux 2.6</title><link>https://rselbach.com/watching-latency-on-linux-26/</link><pubDate>Thu, 23 Sep 2004 20:10:00 +0000</pubDate><guid>https://rselbach.com/watching-latency-on-linux-26/</guid><description>&lt;p&gt;I have been comparing two Linux 2.6 configurations for a workstation: one with kernel preemption enabled and one without it. Looking only at total build time made them appear nearly identical, while using the machine made them feel different. The missing measurement was latency.&lt;/p&gt;
&lt;p&gt;Throughput asks how much work finishes in an interval. Latency asks how long a particular piece of work waits. A kernel build is a reasonable throughput load, but its completion time does not reveal that an audio process waited long enough to miss a deadline or that keyboard input paused for a fraction of a second.&lt;/p&gt;
&lt;p&gt;My crude but useful test runs the same build while playing audio, copying a large file, and interacting with KDE. I record build time, note audio underruns reported by the application, and repeat enough times to distinguish a pattern from one lucky run.&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-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;time make -j2 bzImage
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The O(1) scheduler helps when many tasks are runnable. Its priority arrays and per-CPU run queues keep selection costs predictable, while interactive heuristics favour tasks that sleep and wake frequently. But the scheduler cannot run a task until the current execution context can be switched.&lt;/p&gt;
&lt;p&gt;That is where kernel preemption matters. With it enabled, ordinary kernel code can be preempted when no lock or other critical condition forbids it. An interactive task awakened during a long system call may run sooner. Interrupt handlers and protected sections remain non-preemptible, so a poor driver can still dominate worst-case delay.&lt;/p&gt;
&lt;p&gt;On my machine, enabling preemption does not materially improve the build time. It does reduce occasional input stalls and audio trouble under mixed I/O load. That is the result I expected: better responsiveness, not newly manufactured processor cycles.&lt;/p&gt;
&lt;p&gt;Thread behaviour can alter the picture too. NPTL mutexes stay in userspace when uncontended and use futex operations when a thread must sleep or wake. A heavily contended threaded program may therefore create a very different scheduling load from one with mostly private work. Counting threads alone says little; runnable and blocked states matter.&lt;/p&gt;
&lt;p&gt;I also test without excessive parallelism. Starting far more compile jobs than processors can turn the test into a contest between memory pressure, disk traffic, and scheduling. That may resemble a real workload, but it is a poor way to isolate one mechanism.&lt;/p&gt;
&lt;p&gt;The caveat is that my observations are not hard real-time guarantees. General-purpose Linux still has sources of unbounded or hardware-dependent delay, and an average hides the worst pause. For desktop tuning, however, repeated underrun counts and interaction under a controlled load tell me more than one impressive jobs-per-minute number.&lt;/p&gt;
&lt;p&gt;I am keeping preemption enabled on the workstation and evaluating it separately on servers. A server handling many requests may care about response-time distribution, but a batch machine may prefer the smallest scheduling overhead. There is no universally fast switch. First decide whether the itch is throughput or latency; only then does the benchmark know what question to answer.&lt;/p&gt;</description></item><item><title>Moving a Real Machine from Linux 2.4 to 2.6</title><link>https://rselbach.com/moving-a-real-machine-from-24-to-26/</link><pubDate>Thu, 15 Apr 2004 22:20:00 +0000</pubDate><guid>https://rselbach.com/moving-a-real-machine-from-24-to-26/</guid><description>&lt;p&gt;I have now moved a machine I actually depend upon from Linux 2.4 to 2.6. Test boxes are excellent for discovering that a kernel boots. A working machine is better at discovering the scanner, firewall rule, and obscure filesystem one forgot to test.&lt;/p&gt;
&lt;p&gt;The obvious approach is to install the new kernel and reboot. The correct approach begins with an inventory. I wrote down the storage controller, root filesystem, network devices, sound hardware, mounted filesystems, firewall setup, and every module not supplied by the kernel tree. If a component is required to boot or reach the network, I want its exact name before changing anything.&lt;/p&gt;
&lt;p&gt;I then upgraded the required userspace while still running 2.4. &lt;code&gt;module-init-tools&lt;/code&gt; understands 2.6 modules and supplies compatibility behaviour for a 2.4 boot. Recent &lt;code&gt;procps&lt;/code&gt; tools understand newer &lt;code&gt;/proc&lt;/code&gt; details and NPTL process presentation. Filesystem utilities must support the on-disk filesystems regardless of which kernel initiated the repair.&lt;/p&gt;
&lt;p&gt;The important point is coexistence. I did not overwrite the old kernel image, old modules, or boot entry. My boot loader contains distinct choices, and I tested selecting 2.4 after installing the new tools. A rollback that has never been attempted is a comforting theory.&lt;/p&gt;
&lt;h2 id="building-the-first-kernel"&gt;Building the first kernel&lt;/h2&gt;
&lt;p&gt;I used the 2.4 configuration as a reference, not scripture. &lt;code&gt;make oldconfig&lt;/code&gt; carries choices forward and asks about new options, but subsystems changed between the series. I read the help for storage, input, sound, device filesystems, networking, and processor options instead of accepting every default at speed.&lt;/p&gt;
&lt;p&gt;For the first build, the disk controller and root filesystem went directly into the kernel. This avoids depending on a new module loader and a new initial ramdisk during the same boot. Once the plain configuration worked, I made less essential drivers modular.&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-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;make oldconfig
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;make bzImage
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;make modules
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;make modules_install
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The exact image installation is distribution and architecture dependent, so I copied it using the same procedure as the existing packaged kernel and created a separate boot-loader stanza. I also saved &lt;code&gt;.config&lt;/code&gt; beside the image with a matching name.&lt;/p&gt;
&lt;p&gt;Linux 2.6 uses sysfs for its device model. My startup scripts mount it at &lt;code&gt;/sys&lt;/code&gt;:&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-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mount -t sysfs none /sys
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I kept the existing &lt;code&gt;/dev&lt;/code&gt; arrangement for the first migration. Early &lt;code&gt;udev&lt;/code&gt; is interesting and I intend to test it, but changing kernel, module format, device discovery, and device-node management in one reboot produces a beautiful failure with too many suspects.&lt;/p&gt;
&lt;h2 id="the-first-boot-is-only-the-start"&gt;The first boot is only the start&lt;/h2&gt;
&lt;p&gt;The machine reached a login prompt immediately, which proved almost nothing. I checked &lt;code&gt;dmesg&lt;/code&gt; from beginning to end for timeouts, unknown options, and drivers claiming the wrong hardware. I mounted every local filesystem, activated swap, transferred data over each network interface, and exercised packet filtering.&lt;/p&gt;
&lt;p&gt;Sound moved to ALSA, now part of the kernel tree. Applications using OSS interfaces can often use ALSA&amp;rsquo;s compatibility modules, but mixer state and device permissions still need checking. Silence may mean the channel is muted rather than the driver is broken, an ancient audio tradition maintained for compatibility with human suffering.&lt;/p&gt;
&lt;p&gt;Threaded programs deserve a run under the new combination of kernel and C library. NPTL relies on facilities available in 2.6 and improves both performance and POSIX behaviour, but old monitoring scripts may interpret the changed process display incorrectly. I checked services by function rather than by expecting the old number of &lt;code&gt;ps&lt;/code&gt; lines.&lt;/p&gt;
&lt;p&gt;The firewall required careful review. The netfilter framework remains, but kernel configuration symbols and available matches can differ. Loading an old saved rule set is useful only if all required modules exist and the restore command reports success. A script that ignores errors can leave a machine less protected while printing reassuring startup messages.&lt;/p&gt;
&lt;p&gt;Out-of-tree modules were the only serious obstacle. Internal kernel interfaces changed, and source written for 2.4 often needs a real port rather than a version-number adjustment. I found replacements in the 2.6 tree where possible. For the remaining hardware, I verified current source before making the migration permanent. Binary-only drivers put the schedule entirely in the vendor&amp;rsquo;s hands, which is one reason I avoid depending on them.&lt;/p&gt;
&lt;h2 id="what-changed-in-use"&gt;What changed in use&lt;/h2&gt;
&lt;p&gt;Under a compile and interactive desktop load, the machine is more responsive. The O(1) scheduler&amp;rsquo;s per-CPU run queues and priority arrays make task selection predictable as load grows. Kernel preemption, enabled on this workstation, reduces the delay before an awakened interactive task can run when another process is in preemptible kernel code.&lt;/p&gt;
&lt;p&gt;This does not mean every benchmark is faster. Responsiveness concerns latency; throughput concerns completed work over time. I measured both and accepted a tiny difference in build time in exchange for fewer audio skips and shorter input pauses.&lt;/p&gt;
&lt;p&gt;Sysfs is already useful for diagnosis. Following links from &lt;code&gt;/sys/class&lt;/code&gt; to a device and its driver is clearer than assembling the relationship from several &lt;code&gt;/proc&lt;/code&gt; files and boot messages. It also provides the structured device information that userspace managers such as &lt;code&gt;udev&lt;/code&gt; need.&lt;/p&gt;
&lt;p&gt;My migration rule is now simple: preserve 2.4, upgrade userspace first, boot a minimally clever 2.6 configuration, and add new mechanisms one at a time. Linux 2.6 is ready for this machine, but it did not become ready merely because &lt;code&gt;uname -r&lt;/code&gt; printed the desired number. It became ready when every required workload passed and the old kernel remained one menu selection away.&lt;/p&gt;</description></item><item><title>What Kernel Preemption Actually Does</title><link>https://rselbach.com/what-kernel-preemption-actually-does/</link><pubDate>Thu, 26 Feb 2004 19:25:00 +0000</pubDate><guid>https://rselbach.com/what-kernel-preemption-actually-does/</guid><description>&lt;p&gt;I enabled kernel preemption on my workstation and was rewarded with the usual technical diagnosis: the machine feels nicer. That is useful as an observation and useless as an explanation, so I went back to what the option actually changes.&lt;/p&gt;
&lt;p&gt;Normal process preemption is not new. The scheduler can stop one userspace task and run another. The relevant 2.6 option allows a task executing kernel code to be preempted too, provided it is not in a critical section where switching would be unsafe.&lt;/p&gt;
&lt;p&gt;Suppose a low-priority process enters a long kernel path just before an audio application becomes runnable. Without kernel preemption, the audio task may wait until that path returns to userspace or reaches another scheduling point. With preemption enabled, the scheduler can run the newly awakened task sooner once the current kernel context is in a preemptible state.&lt;/p&gt;
&lt;p&gt;The mechanism depends on keeping count of places where preemption must not occur. Spinlocks and explicit preemption disabling protect critical regions. Interrupt context has its own restrictions. The option does not permit the scheduler to interrupt arbitrary code while it holds shared state halfway through an update. That would improve latency in much the same way removing the brakes improves a car&amp;rsquo;s acceleration.&lt;/p&gt;
&lt;p&gt;For a practical comparison I run a kernel build while playing audio, moving windows, and causing disk activity. I watch for skips and input stalls, but I also time the build. Lower worst-case latency is the target; higher total throughput is not guaranteed.&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-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;time make -j2 bzImage
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On this desktop, preemption reduces the noticeable pauses under mixed load. The difference is most apparent when a background job causes frequent kernel work rather than merely consuming CPU. The O(1) scheduler already decides quickly which task should run; preemption helps the kernel reach a point where that decision can take effect.&lt;/p&gt;
&lt;p&gt;I would not enable it automatically on every server. Preemption introduces scheduling opportunities and some overhead, while a throughput-oriented workload may gain nothing from lower interactive latency. Test the actual workload and look at variance, not only an average benchmark.&lt;/p&gt;
&lt;p&gt;There are also limits. Long non-preemptible sections, interrupt handlers, slow hardware, and drivers can still cause delays. The option narrows one source of latency; it does not turn a general-purpose kernel into a hard real-time system.&lt;/p&gt;
&lt;p&gt;For workstations, audio systems, and other latency-sensitive machines, it is a sensible trade. For a batch server, the default conservative choice may remain better. The nice thing is that the trade is explicit. I can choose responsiveness where I need it without pretending every Linux machine has the same job.&lt;/p&gt;</description></item><item><title>Linux 2.6.0 Arrives</title><link>https://rselbach.com/linux-260-arrives/</link><pubDate>Thu, 18 Dec 2003 23:15:00 +0000</pubDate><guid>https://rselbach.com/linux-260-arrives/</guid><description>&lt;p&gt;Linux 2.6.0 was released yesterday. After the long 2.5 development series and months of test releases, there is finally a version number administrators can put into a plan without the word &amp;ldquo;test&amp;rdquo; in it. This does not mean I am replacing every 2.4 kernel before lunch. It means the migration can become deliberate rather than hypothetical.&lt;/p&gt;
&lt;p&gt;The itch for me is desktop latency. My machine compiles, plays audio, runs KDE, and occasionally receives the unreasonable request to respond to the keyboard at the same time. Linux 2.6 has two important mechanisms for that: the O(1) scheduler and optional kernel preemption.&lt;/p&gt;
&lt;p&gt;The scheduler keeps per-CPU active and expired priority arrays. Selecting the next task does not require scanning a growing list of runnable processes, and interactive tasks receive dynamic priority treatment based largely on sleep behaviour. Preemption goes a step further. When enabled, kernel code that is not in a critical section can be preempted, reducing the time a newly runnable high-priority task waits for a long kernel path to finish.&lt;/p&gt;
&lt;p&gt;Neither feature creates free CPU time. A saturated machine remains saturated, and a bad driver can still ruin the afternoon. What changes is how predictably the system gives urgent work a turn.&lt;/p&gt;
&lt;p&gt;Threading is another substantial improvement. The kernel facilities used by NPTL provide proper thread groups, efficient futex-based synchronization, and saner signal and process semantics than LinuxThreads could offer. A POSIX mutex can complete entirely in userspace when uncontended and enter the kernel only to wait or wake when there is contention. This matters both for performance and for making tools show one multithreaded process as something resembling one multithreaded process.&lt;/p&gt;
&lt;p&gt;The device model may have the largest administrative consequence. Sysfs exposes devices, buses, classes, and drivers as related kernel objects under &lt;code&gt;/sys&lt;/code&gt;. The kernel now has a coherent answer to questions such as &amp;ldquo;what is this device attached to?&amp;rdquo; instead of scattering hints across boot messages and &lt;code&gt;/proc&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That structure makes userspace device management practical. Early work on &lt;code&gt;udev&lt;/code&gt; can listen for hotplug events, consult sysfs, and create device nodes according to userspace policy. I am not ready to discard a working &lt;code&gt;/dev&lt;/code&gt; setup everywhere, but moving naming policy out of the kernel is the right direction. Hardware discovery belongs in the kernel; deciding that a particular camera should have a friendly local name does not.&lt;/p&gt;
&lt;p&gt;There are less glamorous changes that may matter more on servers. The block I/O layer and filesystem scalability have received major work. The kernel supports larger systems, more processors, and more memory without treating each as a surprising edge case. Native ALSA support gives sound users a maintained kernel interface, though moving from OSS drivers may require mixer and application adjustments.&lt;/p&gt;
&lt;h2 id="the-migration-is-not-just-a-kernel-file"&gt;The migration is not just a kernel file&lt;/h2&gt;
&lt;p&gt;My safe upgrade recipe begins by leaving 2.4 intact. I install 2.6 under a distinct name, add a separate boot-loader entry, and verify that selecting the old entry still works. The new kernel image is the easiest part to roll back; userspace and configuration changes require more thought.&lt;/p&gt;
&lt;p&gt;Linux 2.6 modules use a different format, so &lt;code&gt;module-init-tools&lt;/code&gt; must be installed. The tools are designed to coexist with the programs needed by 2.4, which is exactly what a gradual migration needs. Recent versions of &lt;code&gt;procps&lt;/code&gt;, filesystem utilities, and other low-level packages are also worth checking against the distribution&amp;rsquo;s recommendations.&lt;/p&gt;
&lt;p&gt;For a first boot I compile the root disk controller and root filesystem into the kernel. Once the machine boots reliably, I can introduce an initial ramdisk and modularise things. Debugging one new mechanism at a time is slower only until the first failure.&lt;/p&gt;
&lt;p&gt;I also mount sysfs explicitly if the startup scripts do not:&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-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mount -t sysfs none /sys
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then I test the boring list: all filesystems, swap, networking, firewall rules, sound, removable devices, suspend if the machine uses it, and clean shutdown. I compare &lt;code&gt;dmesg&lt;/code&gt; for errors rather than merely noticing that a login prompt appeared. A successful boot proves the boot path, not the rest of the computer.&lt;/p&gt;
&lt;p&gt;Out-of-tree drivers are the largest caveat. The internal kernel interfaces have changed, sometimes substantially, and a driver compiling on 2.4 says nothing about 2.6. Binary-only modules are especially dependent on their vendor producing a matching build. Before an upgrade, inventory these modules with the same seriousness as storage hardware.&lt;/p&gt;
&lt;p&gt;Applications should generally continue to run. This is not a new userspace ABI. Problems are more likely to come from programs that inspect &lt;code&gt;/proc&lt;/code&gt;, rely on old thread display details, expect OSS device behaviour, or invoke module utilities directly.&lt;/p&gt;
&lt;p&gt;My opinion is that 2.6 is a stronger foundation, especially for interactive systems and machines with many tasks or processors. But &lt;code&gt;.0&lt;/code&gt; is a milestone, not a papal declaration of perfection. Production systems deserve a soak period, distribution patches, and testing against their actual workload.&lt;/p&gt;
&lt;p&gt;I will install it quickly on my workstation and slowly on servers. This is not inconsistency. The workstation can inconvenience me; the server can introduce me to several irritated people at once. Linux 2.6.0 is ready for serious testing today, and with cautious migration it should become the ordinary kernel soon enough.&lt;/p&gt;</description></item><item><title>Reading the Device Tree in sysfs</title><link>https://rselbach.com/reading-the-device-tree-in-sysfs/</link><pubDate>Thu, 06 Nov 2003 17:55:00 +0000</pubDate><guid>https://rselbach.com/reading-the-device-tree-in-sysfs/</guid><description>&lt;p&gt;I needed to answer a simple question on a 2.6 test machine: which driver owns this network device? My old habit was to rummage through boot messages, &lt;code&gt;/proc&lt;/code&gt;, and module output until the answer surrendered. The new device model gives us a less archaeological method.&lt;/p&gt;
&lt;p&gt;First, mount &lt;code&gt;sysfs&lt;/code&gt; if the installation has not done it already:&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-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mount -t sysfs none /sys
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The interesting part is not that &lt;code&gt;/sys&lt;/code&gt; contains files. Linux can turn almost anything into files if left unattended. The useful part is the structure. Devices appear according to their physical or logical parentage under &lt;code&gt;/sys/devices&lt;/code&gt;. Buses, such as PCI and USB, have views under &lt;code&gt;/sys/bus&lt;/code&gt;, while &lt;code&gt;/sys/class&lt;/code&gt; groups devices by function, such as network interfaces or block devices.&lt;/p&gt;
&lt;p&gt;These are views of the same kernel objects, joined by symbolic links. For an Ethernet interface named &lt;code&gt;eth0&lt;/code&gt;, this is a useful start:&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-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;readlink /sys/class/net/eth0/device
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ls -l /sys/class/net/eth0/device/driver
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The first link leads back into the device hierarchy. The &lt;code&gt;driver&lt;/code&gt; link, when present, identifies the bound driver. Following parent links tells me which PCI device contains the interface and where that device sits.&lt;/p&gt;
&lt;p&gt;This differs from &lt;code&gt;/proc&lt;/code&gt;. Procfs grew as a place for process information and gradually acquired assorted kernel knobs and device facts. Sysfs is an exported representation of the kernel&amp;rsquo;s device model. One attribute per file is the usual convention, which makes simple scripts possible without parsing a decorative essay from the kernel.&lt;/p&gt;
&lt;p&gt;I am deliberately cautious about those scripts. The 2.6 series is not final yet, and sysfs details can change as interfaces settle. Scripts should consume documented attributes, not depend on directory ordering or infer meaning from every internal-looking name. A readable tree is still an interface, not an invitation to marry the first implementation detail one encounters.&lt;/p&gt;
&lt;p&gt;The larger implication is device management in userspace. If the kernel exposes device identity and events cleanly, a userspace program can create names and permissions according to policy rather than a huge static &lt;code&gt;/dev&lt;/code&gt; or a kernel naming scheme. That work is still young, but sysfs supplies the map it needs.&lt;/p&gt;
&lt;p&gt;For now, it has already replaced several minutes of log-diving with two symbolic links. I consider that progress, even if it has added another virtual filesystem to my vocabulary.&lt;/p&gt;</description></item><item><title>Why the O(1) Scheduler Feels Different</title><link>https://rselbach.com/why-the-o1-scheduler-feels-different/</link><pubDate>Thu, 07 Aug 2003 18:20:00 +0000</pubDate><guid>https://rselbach.com/why-the-o1-scheduler-feels-different/</guid><description>&lt;p&gt;I have been testing the new kernel on a machine that does two incompatible jobs: compile software and remain pleasant enough to use while compiling software. Under load, the 2.6 test kernel feels less sticky than my usual 2.4 setup. The O(1) scheduler is a large part of that.&lt;/p&gt;
&lt;p&gt;The name describes a property, not a performance promise. Choosing the next runnable task takes constant time rather than getting more expensive as the run queue grows. The scheduler keeps arrays of queues indexed by priority, plus a bitmap showing which priorities contain runnable tasks. Finding work becomes a matter of locating the first set bit and taking a task from that queue.&lt;/p&gt;
&lt;p&gt;There are actually two priority arrays per processor: active and expired. A normal task runs for its time slice and then moves to the expired array. When active becomes empty, the kernel swaps the arrays. No grand tour through every process is required.&lt;/p&gt;
&lt;p&gt;That design also avoids one global run queue becoming a lock everybody fights over on an SMP machine. Each processor has its own run queue, and load balancing periodically moves work when the queues become uneven. Per-processor data makes the common path cheaper, although balancing is still real work and certainly not magic.&lt;/p&gt;
&lt;p&gt;Interactive behaviour comes from dynamic priorities. A task that sleeps often, as an editor or terminal usually does while waiting for me, receives favourable treatment when it wakes. A compiler chewing CPU continuously tends to use its slice and wait in the expired array. The scheduler is estimating interactivity from sleep behaviour; applications do not announce that they are important because a human is glaring at them.&lt;/p&gt;
&lt;p&gt;There is a caveat. Heuristics can be fooled, and &amp;ldquo;feels faster&amp;rdquo; is not a benchmark. A database server and a desktop have very different ideas of fairness. I am watching throughput as well as latency and checking for tasks that receive odd priority treatment.&lt;/p&gt;
&lt;p&gt;Still, the mechanism is refreshingly concrete. Constant-time selection, per-CPU queues, and explicit expired work explain the observed behaviour without fairy dust. The desktop remains responsive during a build, the build still finishes, and neither needs a special incantation. That is a scheduler improvement I can actually notice.&lt;/p&gt;</description></item><item><title>Getting Ready for Linux 2.6-test1</title><link>https://rselbach.com/getting-ready-for-linux-26-test1/</link><pubDate>Thu, 17 Jul 2003 21:35:00 +0000</pubDate><guid>https://rselbach.com/getting-ready-for-linux-26-test1/</guid><description>&lt;p&gt;Linus released &lt;code&gt;2.6.0-test1&lt;/code&gt; this week, which means the 2.5 development series has finally put on a tie and is pretending to be suitable for polite company. It is still a test kernel. I would not install it on the machine that pays the bills, but I do want to know what will break before 2.6 becomes ordinary.&lt;/p&gt;
&lt;p&gt;The obvious approach is to keep the current 2.4 kernel installed, add 2.6 beside it, and make the boot loader offer both. Do not replace the known-good entry. This sounds insultingly basic until the new kernel cannot find its root filesystem and the old entry is the difference between five minutes and a rescue disk.&lt;/p&gt;
&lt;p&gt;My first checklist is mostly userspace:&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-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;module-init-tools
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;recent modutils-compatible configuration
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;procps that understands the newer /proc output
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;filesystem tools matching every filesystem in use
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;working boot loader configuration
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The module change is the one most likely to surprise people. Linux 2.6 uses a new module format and the &lt;code&gt;module-init-tools&lt;/code&gt; programs. They can coexist with the old tools, so one installation can boot both 2.4 and 2.6. Check this before rebooting, not while staring at an emergency shell.&lt;/p&gt;
&lt;p&gt;I also build the drivers needed to reach the root filesystem directly into the kernel for the first attempt. That means the disk controller, root filesystem, and anything else between the kernel and &lt;code&gt;/sbin/init&lt;/code&gt;. An initial ramdisk is useful, but removing it from the first experiment makes failures much easier to understand.&lt;/p&gt;
&lt;p&gt;The device model has changed substantially. A mounted &lt;code&gt;sysfs&lt;/code&gt;, normally at &lt;code&gt;/sys&lt;/code&gt;, exposes devices and drivers in a structured tree. Old scripts that scrape &lt;code&gt;/proc&lt;/code&gt; or assume particular module names deserve suspicion. Sound users should note that ALSA is now in the kernel tree; OSS compatibility exists, but that does not make every mixer setup identical.&lt;/p&gt;
&lt;p&gt;Finally, save the exact &lt;code&gt;.config&lt;/code&gt; and the complete build output. &lt;code&gt;make oldconfig&lt;/code&gt; is a convenient starting point, not proof that old choices still mean the same thing. Read every new question that affects storage, filesystems, networking, or the console.&lt;/p&gt;
&lt;p&gt;The kernel has many attractive improvements: preemption, the O(1) scheduler, better threading support, and a much saner device model. None of them will comfort you if the machine does not boot. My plan is boring: test hardware, test userspace, test rollback, then become adventurous. Adventure is much nicer with a working fallback kernel.&lt;/p&gt;</description></item><item><title>Keeping Work Out of a Network Interrupt</title><link>https://rselbach.com/keeping-work-out-of-a-network-interrupt/</link><pubDate>Tue, 27 May 2003 05:47:36 +0000</pubDate><guid>https://rselbach.com/keeping-work-out-of-a-network-interrupt/</guid><description>&lt;p&gt;I put packet bookkeeping into a network device&amp;rsquo;s interrupt handler because the information was conveniently available there. Under load, the machine reminded me that convenience inside interrupt context is borrowed at unreasonable interest.&lt;/p&gt;
&lt;p&gt;The interrupt handler should acknowledge the device, collect enough status to know what happened, move completed descriptors along, and arrange deferred processing where appropriate. It cannot sleep, and time spent there delays other interrupt work. Long parsing, allocation loops, or diagnostic printing are especially poor guests.&lt;/p&gt;
&lt;p&gt;Linux 2.4 offers deferred mechanisms including tasklets and the network receive path&amp;rsquo;s softirq processing. A driver can prepare received skbs and pass them to &lt;code&gt;netif_rx()&lt;/code&gt;, allowing the protocol stack to process packets outside the hard interrupt handler. For driver-specific deferred work, I put the tasklet in the device&amp;rsquo;s private state:&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;struct&lt;/span&gt; sample_dev {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; tasklet_struct rx_tasklet;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/* rings, locks, and device state */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;sample_tasklet&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;unsigned&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;long&lt;/span&gt; data)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; sample_dev &lt;span style="color:#f92672"&gt;*&lt;/span&gt;dev &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; sample_dev &lt;span style="color:#f92672"&gt;*&lt;/span&gt;)data;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;service_completed_packets&lt;/span&gt;(dev);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;sample_init_tasklet&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; sample_dev &lt;span style="color:#f92672"&gt;*&lt;/span&gt;dev)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;tasklet_init&lt;/span&gt;(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;dev&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;rx_tasklet, sample_tasklet,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (&lt;span style="color:#66d9ef"&gt;unsigned&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;long&lt;/span&gt;)dev);
&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;&lt;code&gt;sample_init_tasklet()&lt;/code&gt; runs after &lt;code&gt;dev&lt;/code&gt; is allocated and before the interrupt can schedule &lt;code&gt;dev-&amp;gt;rx_tasklet&lt;/code&gt;. Teardown disables that interrupt source, calls &lt;code&gt;tasklet_kill(&amp;amp;dev-&amp;gt;rx_tasklet)&lt;/code&gt;, and only then frees &lt;code&gt;dev&lt;/code&gt;. The callback therefore never receives the null value that a static &lt;code&gt;DECLARE_TASKLET(..., 0)&lt;/code&gt; would have supplied.&lt;/p&gt;
&lt;p&gt;Shared rings and status fields need protection across the interrupt, deferred function, transmit entry point, and process-context control operations. I use spinlocks where the data can be touched in interrupt context and choose the interrupt-disabling variant when the same processor could otherwise interrupt a lock holder. I keep the protected section short and never call a function that can sleep while holding it.&lt;/p&gt;
&lt;p&gt;There is a balance. Deferring every tiny operation adds overhead and can complicate ordering, while doing everything in the handler harms latency. I measure under sustained receive and transmit load, watch dropped packets, and check that another device&amp;rsquo;s interrupts are not starved.&lt;/p&gt;
&lt;p&gt;Interrupt acknowledgement order is device-specific and worth documenting beside the handler. A level-triggered interrupt that remains asserted can immediately enter again; acknowledging too early on other hardware may lose status that has not been copied. I read the device manual rather than deriving this order from whichever experiment happened not to hang.&lt;/p&gt;
&lt;p&gt;Shutdown receives the same scrutiny. The driver disables the device&amp;rsquo;s interrupt source, prevents new deferred work, frees the IRQ with the required synchronization, and only then releases rings and private data. Otherwise a late tasklet can faithfully process memory that has already acquired a new purpose.&lt;/p&gt;
&lt;p&gt;I want the hard interrupt path to be visibly bounded. One caveat: &lt;code&gt;printk&lt;/code&gt; can completely distort timing here. A hundred helpful messages per second soon become the performance problem, a promotion few debug statements deserve.&lt;/p&gt;</description></item><item><title>Module Parameters in Linux 2.4</title><link>https://rselbach.com/module-parameters-in-linux-24/</link><pubDate>Thu, 16 Jan 2003 08:22:47 +0000</pubDate><guid>https://rselbach.com/module-parameters-in-linux-24/</guid><description>&lt;p&gt;I was rebuilding a test module merely to change its debug level. That is a fine way to turn a ten-second experiment into a small ritual. Linux 2.4 module parameters are a better fit.&lt;/p&gt;
&lt;p&gt;For a simple integer, the old module parameter declaration is compact:&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;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; debug;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;MODULE_PARM&lt;/span&gt;(debug, &lt;span style="color:#e6db74"&gt;&amp;#34;i&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;MODULE_PARM_DESC&lt;/span&gt;(debug, &lt;span style="color:#e6db74"&gt;&amp;#34;enable diagnostic messages&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I can then set it while loading:&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-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;insmod sample.o debug&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The type string tells the module loader how to interpret the value. Other forms support strings and arrays, but I avoid elaborate configuration here. Parameters are useful for hardware choices and diagnostics needed at initialization, not as a private replacement for a proper user interface.&lt;/p&gt;
&lt;p&gt;Validation still belongs in the initialization function. A parameter arriving from &lt;code&gt;insmod&lt;/code&gt; is input, not a promise. If a buffer count must be positive and bounded, the module should reject nonsense with &lt;code&gt;-EINVAL&lt;/code&gt; before allocating resources.&lt;/p&gt;
&lt;p&gt;I also print the effective non-default setting at load time when it materially changes behavior. That makes &lt;code&gt;dmesg&lt;/code&gt; useful when somebody reports that the driver behaves differently on one machine.&lt;/p&gt;
&lt;p&gt;My preference is a quiet default and one obvious parameter for debugging. Five boolean switches rapidly become a combination lock whose correct setting nobody remembers. Recompiling was tedious, but at least it did not accept &lt;code&gt;debug=banana&lt;/code&gt; with a straight face.&lt;/p&gt;</description></item><item><title>Reading an skb Without Regretting It</title><link>https://rselbach.com/reading-an-skb-without-regretting-it/</link><pubDate>Fri, 20 Dec 2002 20:14:39 +0000</pubDate><guid>https://rselbach.com/reading-an-skb-without-regretting-it/</guid><description>&lt;p&gt;I added a packet check to a Linux 2.4 network path and initially treated &lt;code&gt;skb-&amp;gt;data&lt;/code&gt; as if it always began with the header I wanted. It did in my first test. Networking code has a generous way of rewarding that assumption just long enough to become embarrassing.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;sk_buff&lt;/code&gt; carries packet data plus bookkeeping used as the packet moves through layers. Its &lt;code&gt;head&lt;/code&gt;, &lt;code&gt;data&lt;/code&gt;, &lt;code&gt;tail&lt;/code&gt;, and &lt;code&gt;end&lt;/code&gt; pointers describe the allocated area and the currently occupied bytes. Protocol processing can pull a consumed header from the front or push space for a new one without copying the whole packet.&lt;/p&gt;
&lt;p&gt;Before reading a header, I check that the required bytes are present and use the header position appropriate to that point in the stack. In a simple driver receive path, the driver allocates an skb, reserves alignment if needed, copies or maps packet bytes, sets the protocol, and hands it upward:&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;skb &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;dev_alloc_skb&lt;/span&gt;(length &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;);
&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; (&lt;span style="color:#f92672"&gt;!&lt;/span&gt;skb)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;ENOMEM;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;skb_reserve&lt;/span&gt;(skb, &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;memcpy&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;skb_put&lt;/span&gt;(skb, length), packet, length);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;skb&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;dev &lt;span style="color:#f92672"&gt;=&lt;/span&gt; dev;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;skb&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;protocol &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;eth_type_trans&lt;/span&gt;(skb, dev);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;netif_rx&lt;/span&gt;(skb);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Calling &lt;code&gt;skb_put()&lt;/code&gt; extends the valid data at the tail and returns the start of the added area. Writing beyond that area corrupts memory, so the allocation and length must agree. &lt;code&gt;eth_type_trans()&lt;/code&gt; interprets the Ethernet header, sets the protocol, and adjusts the skb for the network layer.&lt;/p&gt;
&lt;p&gt;Ownership changes at &lt;code&gt;netif_rx()&lt;/code&gt;. After handing the skb to the network stack, the driver must not free or inspect it as though it still belongs to the receive routine. Error paths before that handoff must free what they allocated.&lt;/p&gt;
&lt;p&gt;Transmit has the opposite pressure. The driver&amp;rsquo;s &lt;code&gt;hard_start_xmit&lt;/code&gt; receives an skb and must obey the device queue rules. If hardware resources are exhausted, stopping the queue and waking it when descriptors become available is better than quietly dropping ownership conventions. Interrupt and bottom-half paths also require locking suited to their contexts; sleeping locks are not available there.&lt;/p&gt;
&lt;p&gt;I prefer using skb helpers over pointer arithmetic, even when the arithmetic looks obvious. The helpers document whether code is adding, removing, or reserving bytes and retain their checks in debugging builds. The caveat is that not every skb is necessarily laid out as one convenient linear region at every layer, so code must respect the guarantees of its exact call site. Packets are simple on diagrams. The diagrams do not have cache lines.&lt;/p&gt;</description></item><item><title>Building a Module for the Running Kernel</title><link>https://rselbach.com/building-a-module-for-the-running-kernel/</link><pubDate>Tue, 03 Dec 2002 08:41:27 +0000</pubDate><guid>https://rselbach.com/building-a-module-for-the-running-kernel/</guid><description>&lt;p&gt;I compiled a small module against the headers in &lt;code&gt;/usr/include/linux&lt;/code&gt;, loaded it, and received a cheerful list of unresolved symbols. The mistake was treating user-space headers as a kernel build environment.&lt;/p&gt;
&lt;p&gt;A module must be built for the kernel that will load it. I first identify that kernel and locate its configured source tree:&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-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;uname -r
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ls -l /lib/modules/&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;uname -r&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;/build
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On a packaged system the &lt;code&gt;build&lt;/code&gt; link commonly points to the matching kernel sources or prepared headers. The tree must have the configuration and generated headers corresponding to the installed kernel. Merely unpacking a similar 2.4 source archive is not enough.&lt;/p&gt;
&lt;p&gt;For a tiny experiment, the compile command shows the important ingredients:&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-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gcc -D__KERNEL__ -DMODULE -O2 -Wall &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -I/lib/modules/&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;uname -r&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;/build/include &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -c sample.c -o sample.o
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Real module makefiles should take their flags from the target kernel&amp;rsquo;s build setup rather than accumulating copied guesses. Processor options, SMP support, and symbol versioning can all affect the expected object. If the kernel was built with module versions, the build also needs the matching version definitions; disabling the complaint does not make incompatible structures compatible.&lt;/p&gt;
&lt;p&gt;I compare the module and running kernel before loading, then inspect the actual diagnostic:&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-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;insmod sample.o
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dmesg
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;An unresolved symbol may mean the providing facility is not configured, is itself a module not yet loaded, was not exported, or has a different version. &lt;code&gt;depmod -a&lt;/code&gt; and &lt;code&gt;modprobe&lt;/code&gt; handle dependency information for installed modules, while &lt;code&gt;insmod&lt;/code&gt; is useful when I deliberately want the direct error from one test object.&lt;/p&gt;
&lt;p&gt;The important mechanism is that a loadable module links against symbols exported by the running kernel and other loaded modules. It is not a normal program with its own copy of kernel interfaces. Configuration differences therefore matter even when function names look identical.&lt;/p&gt;
&lt;p&gt;Optimization is not optional decoration here. Kernel headers use inline functions and assumptions expected by the normal build flags, so an unusual hand-written command can expose failures absent from an ordinary kernel build. I use the short command only to understand the pieces, then put repeatable work in a makefile tied to the configured tree.&lt;/p&gt;
&lt;p&gt;Before installing, I test removal as well as insertion and confirm that no old object with the same module name is already loaded. Debugging yesterday&amp;rsquo;s module while reading today&amp;rsquo;s source is a surprisingly convincing experience.&lt;/p&gt;
&lt;p&gt;I prefer building against the exact configured tree and letting version checks fail loudly. The caveat is that matching &lt;code&gt;uname -r&lt;/code&gt; is necessary but not magical; a locally rebuilt kernel with the same release string can still differ. Names are labels, not fingerprints, despite what some build directories would like me to believe.&lt;/p&gt;</description></item><item><title>Following a File Through the Linux 2.4 VFS</title><link>https://rselbach.com/following-a-file-through-the-linux-24-vfs/</link><pubDate>Tue, 12 Nov 2002 14:56:03 +0000</pubDate><guid>https://rselbach.com/following-a-file-through-the-linux-24-vfs/</guid><description>&lt;p&gt;I was reading a small filesystem and kept confusing dentries, inodes, and open files. They all seemed to point at roughly the same object until a rename and two simultaneous opens proved otherwise. I finally followed one pathname through the Linux 2.4 VFS and wrote down what each structure represents.&lt;/p&gt;
&lt;p&gt;The VFS is the layer that gives user programs the usual operations such as &lt;code&gt;open&lt;/code&gt;, &lt;code&gt;read&lt;/code&gt;, and &lt;code&gt;stat&lt;/code&gt; while allowing ext2, NFS, procfs, and other filesystems to implement different storage rules. It does this with common objects and tables of function pointers. The useful mental model is not one structure per file. It is several structures per role.&lt;/p&gt;
&lt;p&gt;A filesystem implementation first registers a &lt;code&gt;file_system_type&lt;/code&gt;:&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;static&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;DECLARE_FSTYPE&lt;/span&gt;(sample_type, &lt;span style="color:#e6db74"&gt;&amp;#34;samplefs&amp;#34;&lt;/span&gt;, sample_read_super,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; FS_REQUIRES_DEV);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; __init &lt;span style="color:#a6e22e"&gt;sample_init&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;register_filesystem&lt;/span&gt;(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;sample_type);
&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;In 2.4, mounting leads to the filesystem&amp;rsquo;s &lt;code&gt;read_super&lt;/code&gt; function. It validates or reads the on-disk superblock, fills the VFS &lt;code&gt;super_block&lt;/code&gt;, installs superblock operations, and supplies a root dentry. The superblock represents one mounted filesystem instance, not the filesystem format in general. Two mounted ext2 partitions therefore have separate superblocks while sharing ext2&amp;rsquo;s implementation code.&lt;/p&gt;
&lt;p&gt;The inode represents a filesystem object and its metadata: type, mode, owner, times, size, and operation tables. A filesystem usually embeds or associates its private inode information with the VFS inode. The inode number identifies it within that filesystem. Hard links make the distinction important: several directory names can refer to the same inode.&lt;/p&gt;
&lt;p&gt;The dentry represents a name in a directory and connects pathname lookup to an inode. It belongs to the dentry cache, remembers parent and child relationships, and may even be negative, meaning that a lookup established that a name does not currently exist. A dentry is therefore about a pathname component and cached lookup state, not merely another spelling of inode.&lt;/p&gt;
&lt;p&gt;Suppose a process opens &lt;code&gt;/mnt/notes/todo&lt;/code&gt;. Pathname lookup starts from a root or current directory and walks components. For each component, the VFS uses cached dentries when possible and asks the parent inode&amp;rsquo;s lookup operation when necessary. By the end, it has a dentry for &lt;code&gt;todo&lt;/code&gt; and, if the file exists, an associated inode.&lt;/p&gt;
&lt;p&gt;Opening then creates a &lt;code&gt;file&lt;/code&gt; object for that particular open instance. It records the current offset, access mode, flags, and file operations. Two processes opening the same inode receive distinct &lt;code&gt;file&lt;/code&gt; objects and can have different offsets. After &lt;code&gt;fork&lt;/code&gt;, file descriptors may instead refer to the same open file object, which explains why their offsets can move together.&lt;/p&gt;
&lt;p&gt;The operation tables divide responsibility. Directory inode operations include tasks such as &lt;code&gt;lookup&lt;/code&gt;, &lt;code&gt;create&lt;/code&gt;, &lt;code&gt;link&lt;/code&gt;, and &lt;code&gt;rename&lt;/code&gt;. File operations include &lt;code&gt;read&lt;/code&gt;, &lt;code&gt;write&lt;/code&gt;, &lt;code&gt;llseek&lt;/code&gt;, &lt;code&gt;open&lt;/code&gt;, and &lt;code&gt;release&lt;/code&gt;. Superblock operations deal with the mounted instance, including writing metadata and releasing it. The VFS calls through these tables without needing ext2 rules in generic pathname code.&lt;/p&gt;
&lt;p&gt;A tiny read method illustrates another boundary:&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;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;ssize_t&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;sample_read&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; file &lt;span style="color:#f92672"&gt;*&lt;/span&gt;file, &lt;span style="color:#66d9ef"&gt;char&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;buf,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;size_t&lt;/span&gt; count, &lt;span style="color:#66d9ef"&gt;loff_t&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;ppos)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;char&lt;/span&gt; text[] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;hello&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;size_t&lt;/span&gt; left;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&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; (&lt;span style="color:#f92672"&gt;*&lt;/span&gt;ppos &lt;span style="color:#f92672"&gt;&amp;gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;sizeof&lt;/span&gt;(text) &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; left &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;sizeof&lt;/span&gt;(text) &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;ppos;
&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; (count &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; left)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; count &lt;span style="color:#f92672"&gt;=&lt;/span&gt; left;
&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; (&lt;span style="color:#a6e22e"&gt;copy_to_user&lt;/span&gt;(buf, text &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;ppos, count))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;EFAULT;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;ppos &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; count;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; count;
&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;The method receives the open &lt;code&gt;file&lt;/code&gt;, not just the inode, because position and open flags belong to the open instance. It uses &lt;code&gt;copy_to_user()&lt;/code&gt; because a user pointer cannot be treated as an ordinary kernel pointer. Returning zero at end of file is part of the read contract, not an error.&lt;/p&gt;
&lt;p&gt;Caching makes lifetime rules less obvious. Closing the last file descriptor does not imply that the inode or dentry disappears immediately; the caches may retain them. Conversely, unlinking a name removes a directory entry but an already open file can remain usable until its final reference is released. Reference counts and VFS helper functions, rather than guesses about user-visible names, govern these lifetimes.&lt;/p&gt;
&lt;p&gt;Locking also follows roles. Operations changing a directory need the directory inode&amp;rsquo;s semaphore according to the VFS calling rules, while filesystem-private data may need its own protection. I do not add locks at random around every pointer. I first determine which VFS locks are already held for that operation and what shared state remains uncovered. Too little locking corrupts data; too much can deadlock, which is corruption with better manners.&lt;/p&gt;
&lt;p&gt;My preferred debugging method is to trace one operation and print object addresses and names carefully: mount, lookup, open, read, release, and unmount. A toy in-memory filesystem is especially educational because there is no disk format to distract from the object relationships.&lt;/p&gt;
&lt;p&gt;The concise version is: a superblock is a mount, an inode is an object, a dentry is a cached name, and a file is an open instance. It is not the whole VFS, but it is enough to stop asking an inode for an open offset. That alone made the source considerably less mysterious.&lt;/p&gt;</description></item><item><title>Waking a Process in Linux 2.4</title><link>https://rselbach.com/waking-a-process-in-linux-24/</link><pubDate>Wed, 16 Oct 2002 06:38:21 +0000</pubDate><guid>https://rselbach.com/waking-a-process-in-linux-24/</guid><description>&lt;p&gt;I had a character driver polling a flag in a loop. It worked, if one defines working as consuming a processor while waiting for a byte that might arrive next Tuesday. A wait queue is the proper mechanism.&lt;/p&gt;
&lt;p&gt;The process prepares to sleep on a queue, checks the condition, and asks the scheduler to run something else. The producer changes the condition and wakes sleepers. In 2.4 code, the shape can be written with the wait-event helpers:&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;static&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;DECLARE_WAIT_QUEUE_HEAD&lt;/span&gt;(read_wait);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; data_ready;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&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; (&lt;span style="color:#a6e22e"&gt;wait_event_interruptible&lt;/span&gt;(read_wait, data_ready))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;ERESTARTSYS;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When data becomes available:&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;data_ready &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;wake_up_interruptible&lt;/span&gt;(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;read_wait);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The condition is the important part. Wakeups are hints to run and check state, not ownership of an event. A process may wake because of a signal, another reader may consume the data first, or the condition may change again before this process runs. The helper loops around the test, which avoids treating a wakeup as a guarantee.&lt;/p&gt;
&lt;p&gt;For interruptible sleep, a pending signal returns control to the caller. Driver code should propagate the interruption rather than quietly restarting a complicated operation itself. User space can then decide what to do.&lt;/p&gt;
&lt;p&gt;The producer and consumer still need proper locking around shared state. A wait queue arranges sleeping and waking; it does not make &lt;code&gt;data_ready&lt;/code&gt; and the associated buffer safe on a multiprocessor machine. I protect the state with the lock appropriate to all its callers, taking care not to sleep while holding a spinlock.&lt;/p&gt;
&lt;p&gt;Underneath, the scheduler tracks each process in a &lt;code&gt;task_struct&lt;/code&gt;, including its state. A sleeping task is not selected to run until it is made runnable again. This is why sleeping is so much cheaper than repeatedly checking a flag, and why calling a sleeping function from interrupt context is forbidden: there is no process context there to put to sleep.&lt;/p&gt;
&lt;p&gt;Nonblocking reads need a parallel path. If no data is ready and the file was opened with &lt;code&gt;O_NONBLOCK&lt;/code&gt;, I return &lt;code&gt;-EAGAIN&lt;/code&gt; instead of joining the wait queue. The same readiness condition should drive &lt;code&gt;poll&lt;/code&gt; so programs using &lt;code&gt;select&lt;/code&gt; or &lt;code&gt;poll&lt;/code&gt; observe exactly what a blocking read would observe. Three slightly different definitions of “ready” produce wonderfully intermittent programs.&lt;/p&gt;
&lt;p&gt;I prefer wait queues over homemade polling even when the first version seems simpler. The caveat is to make the condition explicit and recheck it under the same synchronization used by the producer. Otherwise the code gains a pleasant nap and an occasional missed wakeup, usually during demonstrations.&lt;/p&gt;</description></item><item><title>A Linux 2.4 Module with a Clean Exit</title><link>https://rselbach.com/a-linux-24-module-with-a-clean-exit/</link><pubDate>Fri, 27 Sep 2002 19:25:34 +0000</pubDate><guid>https://rselbach.com/a-linux-24-module-with-a-clean-exit/</guid><description>&lt;p&gt;I unloaded a test module and discovered that its timer was still quite enthusiastic. The machine survived, but only because the callback lost its race with removal. That is not a synchronization strategy I want to defend.&lt;/p&gt;
&lt;p&gt;A small 2.4 module has an initialization function and an exit function:&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:#75715e"&gt;#include&lt;/span&gt; &lt;span style="color:#75715e"&gt;&amp;lt;linux/init.h&amp;gt;&lt;/span&gt;&lt;span style="color:#75715e"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#include&lt;/span&gt; &lt;span style="color:#75715e"&gt;&amp;lt;linux/module.h&amp;gt;&lt;/span&gt;&lt;span style="color:#75715e"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; __init &lt;span style="color:#a6e22e"&gt;sample_init&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;printk&lt;/span&gt;(KERN_INFO &lt;span style="color:#e6db74"&gt;&amp;#34;sample: loaded&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; __exit &lt;span style="color:#a6e22e"&gt;sample_exit&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;printk&lt;/span&gt;(KERN_INFO &lt;span style="color:#e6db74"&gt;&amp;#34;sample: unloaded&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;module_init&lt;/span&gt;(sample_init);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;module_exit&lt;/span&gt;(sample_exit);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;MODULE_LICENSE&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;GPL&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Initialization should acquire resources in a known order. If the third registration fails, it must release the first two before returning an error. The exit path then releases every successfully acquired resource in the reverse order. This includes interrupt handlers, timers, task queues, proc entries, device numbers, and allocated memory.&lt;/p&gt;
&lt;p&gt;The subtle part is activity that may still enter the module. Before freeing data, I stop new work and wait or synchronize with work already in progress. For a timer, &lt;code&gt;del_timer_sync()&lt;/code&gt; is preferable when the callback might be running on another processor. For an interrupt, I disable the device&amp;rsquo;s source as appropriate and call &lt;code&gt;free_irq()&lt;/code&gt;, which synchronizes with handlers before returning.&lt;/p&gt;
&lt;p&gt;Module use counts provide another guard in 2.4 code. An open file operation commonly increments the count and release decrements it, preventing removal while file operations can still call module text. The exact helper depends on the code and kernel version, but the principle is stable: every path that lends out an active reference must take and return it symmetrically.&lt;/p&gt;
&lt;p&gt;Registration order can reduce cleanup work. I initialize private locks and memory before exposing a device entry that another process can open. The externally visible registration comes last. During removal I reverse that idea: withdraw the entry first so no new caller arrives, then drain existing work and release private state. An object should not become public while it is only half constructed.&lt;/p&gt;
&lt;p&gt;I keep a small state table while reviewing the code: which resources exist after each possible failure, and which callbacks can still run. If two exits require different cleanup, explicit labels in reverse order are clearer than one flag for every allocation.&lt;/p&gt;
&lt;p&gt;I test the unhappy path too. Repeated &lt;code&gt;insmod&lt;/code&gt; and &lt;code&gt;rmmod&lt;/code&gt;, an intentional allocation failure, and unloading while a user process has the device open find more defects than one successful load at boot.&lt;/p&gt;
&lt;p&gt;My preference is an exit routine so boring that each line is the mirror of initialization. The caveat is that reversal alone does not solve concurrency; first make callbacks impossible, then free what they used. Memory is patient. A callback into unloaded text is less so.&lt;/p&gt;</description></item></channel></rss>