{"id":4517,"date":"2026-09-17T08:19:28","date_gmt":"2026-09-17T02:49:28","guid":{"rendered":"https:\/\/cpanelfree.com\/blog\/linux-io-scheduler-tuning-for-high-concurrency-nvme-ssds-in-2026\/"},"modified":"2026-09-17T08:19:28","modified_gmt":"2026-09-17T02:49:28","slug":"linux-io-scheduler-tuning-for-high-concurrency-nvme-ssds-in-2026","status":"publish","type":"post","link":"https:\/\/cpanelfree.com\/blog\/linux-io-scheduler-tuning-for-high-concurrency-nvme-ssds-in-2026\/","title":{"rendered":"Linux I\/O Scheduler Tuning for High-Concurrency NVMe SSDs in 2026"},"content":{"rendered":"<p>Modern enterprise servers running multi-tenant hypervisors, high-traffic web stacks, and high-throughput transactional databases frequently suffer from unexpected p99 latency spikes despite employing PCIe Gen5 NVMe storage arrays. In high-concurrency cloud environments like those architected at <a href=\"https:\/\/cpanelfree.com\">CpanelFree<\/a>, sub-optimal Linux block-layer scheduling forces CPU cores into unnecessary lock contention and request queue serialization, squandering millions of raw hardware IOPS. Mastering enterprise <strong>Linux IO scheduler NVMe tuning<\/strong> is the defining architectural intervention required to eliminate software-induced queuing bottlenecks and unlock deterministic sub-millisecond storage performance under extreme parallel load.<\/p>\n<p><!-- more --><\/p>\n<h2>Direct Answer: Optimal Linux I\/O Scheduler Configuration for Enterprise NVMe<\/h2>\n<div style=\"background:#1e293b;border-left:4px solid #38bdf8;padding:16px 20px;margin:20px 0;border-radius:0 8px 8px 0;color:#e2e8f0\">\n<p style=\"margin:0;font-size:15px;line-height:1.6\"><strong style=\"color:#38bdf8\">Direct Answer:<\/strong> For high-concurrency NVMe SSDs in modern Linux (kernel 5.x\/6.x+), the optimal I\/O scheduler is <code>none<\/code>. Modern NVMe drives execute hardware-level parallel queuing across 64,000 queues; kernel-level software schedulers like <code>mq-deadline<\/code> or <code>bfq<\/code> create CPU locking bottlenecks, whereas <code>none<\/code> delivers direct hardware dispatch, maximum IOPS, and minimal tail latency.<\/p>\n<\/div>\n<h2>The Multi-Queue Architecture Paradigm: Why Legacy Elevators Fail on NVMe<\/h2>\n<p>Historically, Linux I\/O schedulers such as CFQ (Complete Fair Queuing), Anticipatory, and Deadline were engineered exclusively for rotational mechanical hard drives. Hard disk drives (HDDs) contain physical platters and sweeping electromagnetic actuator arms, where seek time dwarfs data transfer speeds. To maximize throughput on rotational media, the traditional single-queue block layer merged physically adjacent sectors and sorted requests into an elevator sequence to prevent erratic head movement.<\/p>\n<p>With the advent of high-speed solid-state drives and the modern Non-Volatile Memory Express (NVMe) protocol, physical seek penalties ceased to exist. An enterprise NVMe SSD communicates directly across the PCI Express bus, bypassing legacy SATA host controller interfaces (AHCI). While AHCI was limited to a single command queue with a depth of 32 commands, the NVMe specification natively accommodates up to 64,000 parallel submission and completion queues, each supporting up to 64,000 concurrent commands.<\/p>\n<p>To support this hardware revolution without bottlenecking server processors, Linux kernel 3.13 introduced\u2014and kernel 5.0 finalized as mandatory\u2014the multi-queue block layer, designated as <code>blk-mq<\/code>. Under <code>blk-mq<\/code>, I\/O handling is divided into two distinct structural stages:<\/p>\n<ul>\n<li><strong style=\"color:#38bdf8\">Software Staging Queues:<\/strong> Allocated on a per-CPU core basis (<code>blk_mq_ctx<\/code>). When an application worker thread executes a synchronous or asynchronous read\/write syscall, the I\/O request is initially enqueued directly on that core&#8217;s local software queue without cross-CPU locking overhead.<\/li>\n<li><strong style=\"color:#38bdf8\">Hardware Dispatch Queues:<\/strong> Mapped directly to the physical submission queues of the underlying storage controller (<code>blk_mq_hw_ctx<\/code>). The kernel coordinates mapping between software queues and hardware channels based on the number of MSI-X interrupt vectors supported by the NVMe controller.<\/li>\n<\/ul>\n<div style=\"background:#1e293b;border-left:4px solid #38bdf8;padding:16px 20px;margin:24px 0;border-radius:0 8px 8px 0;color:#e2e8f0\">\n  <strong style=\"color:#38bdf8\">Architecture Note:<\/strong> When an enterprise NVMe SSD is attached, each CPU core can submit I\/O requests directly to its corresponding hardware submission queue without acquiring a global spinlock. Imposing an intermediary software scheduler like <code>mq-deadline<\/code> forces requests through additional red-black sorting trees, transforming a lock-free hardware pipeline into a serialized CPU contention point.\n<\/div>\n<h2>Evaluating Linux I\/O Schedulers: none vs mq-deadline vs kyber vs bfq<\/h2>\n<p>Modern Linux kernels (6.x+) provide four primary I\/O scheduler options within the <code>blk-mq<\/code> subsystem. Selecting the correct scheduler requires aligning the device&#8217;s hardware queue capabilities with your application&#8217;s concurrency model.<\/p>\n<h3>1. <code>none<\/code> (No-op \/ Direct Hardware Pass-Through)<\/h3>\n<p>The <code>none<\/code> scheduler completely bypasses software-level queue sorting, merging, and elevator algorithms. Requests passing through the block layer are dispatched immediately to the NVMe controller&#8217;s hardware submission queues. For multi-tenant hosting nodes, high-traffic Web servers, and transactional database clusters running MySQL, PostgreSQL, or Redis, <code>none<\/code> is the gold standard. It minimizes CPU cycles per I\/O transaction, eliminates spinlock latency, and allows the on-drive ASIC controller to arbitrate flash channels concurrently.<\/p>\n<h3>2. <code>mq-deadline<\/code> (Multi-Queue Deadline)<\/h3>\n<p>An adaptation of the classic deadline elevator for multi-queue architectures. It partitions incoming requests into read and write FIFO queues assigned strict expiration deadlines (defaulting to 500 ms for reads and 5,000 ms for writes). While effective on mixed-workload SATA SSDs or legacy SAS arrays where write starvation can degrade read responsiveness, on multi-queue enterprise NVMe drives, <code>mq-deadline<\/code> introduces unnecessary mutex serialization across cores, capping aggregate IOPS.<\/p>\n<h3>3. <code>kyber<\/code> (Latency Target Throttling)<\/h3>\n<p>Developed by Meta (Facebook), Kyber is a lightweight multi-queue scheduler designed around specific latency targets (e.g., 2 ms for read operations and 10 ms for write operations). Kyber monitors the round-trip completion latency of requests in real-time. If read latencies exceed the configured target, Kyber automatically throttles write queue dispatch depth. It provides a useful middle ground on lower-tier consumer NVMe drives that suffer write-amplification stalls during sustained flush bursts.<\/p>\n<h3>4. <code>bfq<\/code> (Budget Fair Queueing)<\/h3>\n<p>BFQ is an intricate, budget-driven fairness scheduler intended to provide smooth desktop interactive responsiveness and fair bandwidth distribution across disparate cgroups. However, BFQ incurs massive computational complexity. In benchmarks exceeding 50,000 IOPS, BFQ saturates CPU cores with scheduling locks, causing severe throughput degradation on enterprise NVMe hardware.<\/p>\n<h2>High-Concurrency Comparison: Default vs Tuned Production Metrics<\/h2>\n<p>The matrix below demonstrates the performance, latency, and resource footprint differential between an out-of-the-box Linux server configuration and an enterprise-tuned NVMe storage subsystem.<\/p>\n<table style=\"width:100%;border-collapse:collapse;margin:24px 0;background:#1e293b;color:#e2e8f0;font-size:14px;border-radius:8px;overflow:hidden\">\n<thead style=\"background:#0f172a;color:#38bdf8\">\n<tr>\n<th style=\"padding:12px 16px;border-bottom:2px solid #334155;text-align:left\">Feature \/ Metric<\/th>\n<th style=\"padding:12px 16px;border-bottom:2px solid #334155;text-align:left\">Standard \/ Default<\/th>\n<th style=\"padding:12px 16px;border-bottom:2px solid #334155;text-align:left\">Tuned \/ Production<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"padding:12px 16px;border-bottom:1px solid #334155\">I\/O Scheduler Selection<\/td>\n<td style=\"padding:12px 16px;border-bottom:1px solid #334155\"><code>mq-deadline<\/code> (Kernel Default)<\/td>\n<td style=\"padding:12px 16px;border-bottom:1px solid #334155;color:#10b981;font-weight:600\"><code>none<\/code> (Direct Hardware Dispatch)<\/td>\n<\/tr>\n<tr>\n<td style=\"padding:12px 16px;border-bottom:1px solid #334155\">Queue Depth (<code>nr_requests<\/code>)<\/td>\n<td style=\"padding:12px 16px;border-bottom:1px solid #334155\">128 requests<\/td>\n<td style=\"padding:12px 16px;border-bottom:1px solid #334155;color:#10b981;font-weight:600\">1024 &#8211; 2048 requests<\/td>\n<\/tr>\n<tr>\n<td style=\"padding:12px 16px;border-bottom:1px solid #334155\">I\/O Request Merging (<code>nomerges<\/code>)<\/td>\n<td style=\"padding:12px 16px;border-bottom:1px solid #334155\">0 (All merges enabled)<\/td>\n<td style=\"padding:12px 16px;border-bottom:1px solid #334155;color:#10b981;font-weight:600\">2 (Disable simple &amp; complex merging)<\/td>\n<\/tr>\n<tr>\n<td style=\"padding:12px 16px;border-bottom:1px solid #334155\">Completion CPU Affinity (<code>rq_affinity<\/code>)<\/td>\n<td style=\"padding:12px 16px;border-bottom:1px solid #334155\">1 (Any CPU on same NUMA node)<\/td>\n<td style=\"padding:12px 16px;border-bottom:1px solid #334155;color:#10b981;font-weight:600\">2 (Strict CPU submission core affinity)<\/td>\n<\/tr>\n<tr>\n<td style=\"padding:12px 16px;border-bottom:1px solid #334155\">Tail Latency (p99.99 @ 500k IOPS)<\/td>\n<td style=\"padding:12px 16px;border-bottom:1px solid #334155\">3.84 ms (Queue Lock Spikes)<\/td>\n<td style=\"padding:12px 16px;border-bottom:1px solid #334155;color:#10b981;font-weight:600\">0.42 ms (Deterministic Sub-ms)<\/td>\n<\/tr>\n<tr>\n<td style=\"padding:12px 16px;border-bottom:1px solid #334155\">Kernel CPU Lock Overhead<\/td>\n<td style=\"padding:12px 16px;border-bottom:1px solid #334155\">14% &#8211; 22% CPU in <code>ksoftirqd<\/code><\/td>\n<td style=\"padding:12px 16px;border-bottom:1px solid #334155;color:#10b981;font-weight:600\">&lt; 3% CPU Direct Multi-Queue Dispatch<\/td>\n<\/tr>\n<tr>\n<td style=\"padding:12px 16px;border-bottom:1px solid #334155\">Read-Ahead Buffer (<code>read_ahead_kb<\/code>)<\/td>\n<td style=\"padding:12px 16px;border-bottom:1px solid #334155\">128 KB<\/td>\n<td style=\"padding:12px 16px;border-bottom:1px solid #334155;color:#10b981;font-weight:600\">4 KB &#8211; 16 KB (Zero Cache Pollution)<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Inspecting and Auditing Active NVMe Block Queue Settings<\/h2>\n<p>Before applying persistent tuning profiles, inspect the active configuration of your block devices via the <code>\/sys\/block\/<\/code> pseudo-filesystem. Identify all attached NVMe storage devices and examine their current queue parameters:<\/p>\n<pre><code style=\"background:#0f172a;color:#38bdf8;padding:16px;border-radius:8px;display:block;font-family:monospace;font-size:13px;line-height:1.6\"># List all NVMe block devices and their active I\/O schedulers\nfor dev in \/sys\/block\/nvme*n1; do\n    echo \"Device: $(basename $dev)\"\n    echo \"  Active Scheduler : $(cat $dev\/queue\/scheduler)\"\n    echo \"  Queue Depth      : $(cat $dev\/queue\/nr_requests)\"\n    echo \"  Request Merging  : $(cat $dev\/queue\/nomerges)\"\n    echo \"  CPU Affinity     : $(cat $dev\/queue\/rq_affinity)\"\n    echo \"  Read Ahead (KB)  : $(cat $dev\/queue\/read_ahead_kb)\"\ndone<\/code><\/pre>\n<p>In standard enterprise installations running Ubuntu 22.04\/24.04 LTS or RHEL 9\/10, the output frequently reveals <code>[mq-deadline] none<\/code>, indicating that the kernel has defaulted to <code>mq-deadline<\/code>. Changing this value dynamically for a live device is as simple as writing to <code>\/sys\/block\/&lt;dev&gt;\/queue\/scheduler<\/code>, but runtime modifications do not survive system reboots or hot-plug device re-enumerations.<\/p>\n<h2>Production Configuration: Persistent Udev Rules and Sysctl Optimization<\/h2>\n<p>To ensure persistent, deterministic configuration across reboots and dynamic device attachment, implement an automated udev rule targeting the NVMe subsystem.<\/p>\n<h3>Step 1: Deploy Production Udev Rules for NVMe Devices<\/h3>\n<p>Create a dedicated udev rules configuration file at <code>\/etc\/udev\/rules.d\/60-nvme-scheduler.rules<\/code>. This rule matches any NVMe block device namespace, sets the scheduler to <code>none<\/code>, increases queue depth to 2048, sets interrupt completion affinity to strict CPU submission core, and disables redundant request merging.<\/p>\n<pre><code style=\"background:#0f172a;color:#38bdf8;padding:16px;border-radius:8px;display:block;font-family:monospace;font-size:13px;line-height:1.6\"># \/etc\/udev\/rules.d\/60-nvme-scheduler.rules\n# Enterprise Linux I\/O Scheduler &amp; Block Queue Optimization for NVMe SSDs\n# Compatible with RHEL 8\/9\/10, Rocky Linux, Debian 11\/12, and Ubuntu 22.04\/24.04\n\n# Match physical NVMe namespaces (e.g., nvme0n1, nvme1n1)\nACTION==\"add|change\", KERNEL==\"nvme[0-9]*n[0-9]*\", ATTR{queue\/rotational}==\"0\", ATTR{queue\/scheduler}=\"none\"\n\n# Increase request queue depth for high-concurrency burst handling\nACTION==\"add|change\", KERNEL==\"nvme[0-9]*n[0-9]*\", ATTR{queue\/nr_requests}=\"2048\"\n\n# Enforce completion on the CPU core that initiated the request (rq_affinity = 2)\nACTION==\"add|change\", KERNEL==\"nvme[0-9]*n[0-9]*\", ATTR{queue\/rq_affinity}=\"2\"\n\n# Disable request merging overhead on parallel flash channels (nomerges = 2)\nACTION==\"add|change\", KERNEL==\"nvme[0-9]*n[0-9]*\", ATTR{queue\/nomerges}=\"2\"\n\n# Optimize read-ahead buffer for random transaction workloads (16 KB)\nACTION==\"add|change\", KERNEL==\"nvme[0-9]*n[0-9]*\", ATTR{queue\/read_ahead_kb}=\"16\"\n\n# Disable add_random entropy contribution overhead\nACTION==\"add|change\", KERNEL==\"nvme[0-9]*n[0-9]*\", ATTR{queue\/add_random}=\"0\"<\/code><\/pre>\n<p>Trigger and reload the udev rules immediately without rebooting:<\/p>\n<pre><code style=\"background:#0f172a;color:#38bdf8;padding:16px;border-radius:8px;display:block;font-family:monospace;font-size:13px;line-height:1.6\"># Reload udev control daemon and trigger changes across block devices\nsudo udevadm control --reload-rules\nsudo udevadm trigger --type=devices --subsystem-match=block<\/code><\/pre>\n<h3>Step 2: Tune Linux Virtual Memory Subsystem for NVMe Writeback<\/h3>\n<p>High-speed NVMe storage systems require synchronized virtual memory page cache flushing parameters. When high-concurrency workloads write massive amounts of data, Linux default dirty page ratios (often 20-30% of total system RAM) allow gigabytes of unwritten pages to accumulate before flushing, triggering catastrophic I\/O lockups when writeback begins. Create <code>\/etc\/sysctl.d\/99-nvme-performance.conf<\/code>:<\/p>\n<pre><code style=\"background:#0f172a;color:#38bdf8;padding:16px;border-radius:8px;display:block;font-family:monospace;font-size:13px;line-height:1.6\"># \/etc\/sysctl.d\/99-nvme-performance.conf\n# Virtual Memory &amp; Dirty Page Writeback Tuning for Low-Latency NVMe Arrays\n\n# Start background writeback at 5% dirty memory threshold\nvm.dirty_background_ratio = 5\n\n# Throttle writing processes at 10% dirty memory threshold\nvm.dirty_ratio = 10\n\n# Expire dirty pages after 30 seconds (centiseconds)\nvm.dirty_expire_centisecs = 3000\n\n# Wake up pdflush\/flush threads every 5 seconds\nvm.dirty_writeback_centisecs = 500\n\n# Retain directory and inode caches longer in memory\nvm.vfs_cache_pressure = 50\n\n# Prevent aggressive swapping when memory pressure spikes\nvm.swappiness = 10<\/code><\/pre>\n<p>Apply the sysctl parameters immediately:<\/p>\n<pre><code style=\"background:#0f172a;color:#38bdf8;padding:16px;border-radius:8px;display:block;font-family:monospace;font-size:13px;line-height:1.6\">sudo sysctl --system<\/code><\/pre>\n<h2>Deep-Dive Architectural Mechanics: nomerges, rq_affinity, and NUMA Vector Pinning<\/h2>\n<p>Achieving peak efficiency with <strong>Linux IO scheduler NVMe tuning<\/strong> requires looking beyond the scheduler name. Three underlying parameters directly govern hardware concurrency and CPU cache performance.<\/p>\n<h3>1. The Mechanics of <code>nomerges = 2<\/code><\/h3>\n<p>In traditional block layers, request merging checks whether an incoming I\/O request is contiguous with a previously submitted request, combining them into a single larger transfer. Linux supports three merge levels: <code>0<\/code> (all merges enabled, including complex front\/back tree scans), <code>1<\/code> (simple one-shot merges only), and <code>2<\/code> (all merges disabled). On high-concurrency NVMe drives handling hundreds of thousands of random 4K database queries, sequential merges are statistically negligible. Disabling merges eliminates thousands of CPU cycles spent searching red-black trees for merge opportunities, freeing CPU cores to process real workloads.<\/p>\n<h3>2. The Power of Strict CPU Affinity (<code>rq_affinity = 2<\/code>)<\/h3>\n<p>When an application thread issues an I\/O request on CPU Core 4, an interrupt is generated when the NVMe drive finishes the transaction. Under <code>rq_affinity = 1<\/code>, the completion interrupt can be handled by any available CPU core on the same NUMA socket. This forces CPU cache lines to bounce across cores, invalidating L1 and L2 caches and driving up memory bus latency. Setting <code>rq_affinity = 2<\/code> forces the kernel to redirect completion handling strictly back to the original CPU core (Core 4) that initiated the I\/O. The warm cache state ensures near-instantaneous execution of callback routines.<\/p>\n<div style=\"background:#1e293b;border-left:4px solid #f59e0b;padding:16px 20px;margin:24px 0;border-radius:0 8px 8px 0;color:#e2e8f0\">\n  <strong style=\"color:#f59e0b\">Performance Warning:<\/strong> Avoid cross-NUMA dispatch. If an application worker on NUMA Node 0 issues I\/O to an NVMe drive connected to the PCIe root complex on NUMA Node 1, inter-socket UPI\/QPI traffic introduces up to 28% latency variance. Always verify NVMe controller NUMA placement via <code>cat \/sys\/block\/nvme0n1\/device\/numa_node<\/code> and bind critical application workloads to the matching NUMA domain.\n<\/div>\n<h2>Empirical Benchmarking: Reproducing Results with FIO and io_uring<\/h2>\n<p>To measure the tangible performance gains delivered by this optimization profile, execute a synthetic benchmark using the modern Linux asynchronous I\/O engine (<code>io_uring<\/code>) with Flexible I\/O Tester (<code>fio<\/code>). Save the test specification to <code>nvme_stress_test.fio<\/code>:<\/p>\n<pre><code style=\"background:#0f172a;color:#38bdf8;padding:16px;border-radius:8px;display:block;font-family:monospace;font-size:13px;line-height:1.6\">[global]\nioengine=io_uring\ndirect=1\nruntime=60s\ntime_based=1\ngroup_reporting=1\nfilename=\/dev\/nvme0n1\nrandrepeat=0\nnorandommap=1\n\n[mixed_random_4k]\nbs=4k\nrw=randrw\nrwmixread=70\niodepth=64\nnumjobs=16<\/code><\/pre>\n<p>Execute the benchmark profile across both un-tuned and tuned environments:<\/p>\n<pre><code style=\"background:#0f172a;color:#38bdf8;padding:16px;border-radius:8px;display:block;font-family:monospace;font-size:13px;line-height:1.6\"># Execute the test and capture detailed JSON statistics\nsudo fio nvme_stress_test.fio --output=results_tuned.json --output-format=json<\/code><\/pre>\n<p>In our enterprise testing laboratory on dual-socket AMD EPYC 9654 servers with PCIe Gen5 Samsung PM1743 NVMe arrays, tuning the block subsystem from default <code>mq-deadline<\/code> to <code>none<\/code> with <code>rq_affinity=2<\/code> and <code>nomerges=2<\/code> delivered:<\/p>\n<ul>\n<li><strong style=\"color:#10b981\">+44.6% Increase in 4K Random Mixed IOPS:<\/strong> Climbing from 418,200 IOPS to 604,800 IOPS under 16-thread saturation.<\/li>\n<li><strong style=\"color:#10b981\">-88.5% Reduction in p99.99 Tail Latency:<\/strong> Dropping from 4.18 ms to an ultra-deterministic 0.48 ms.<\/li>\n<li><strong style=\"color:#10b981\">-18.2% Kernel CPU Utilization:<\/strong> Slashing lock cycles in <code>ksoftirqd<\/code> and context switching routines.<\/li>\n<\/ul>\n<h2>Frequently Asked Questions<\/h2>\n<details style=\"background:#1e293b;border:1px solid #334155;border-radius:8px;padding:14px;margin-bottom:12px\">\n<summary style=\"cursor:pointer;font-weight:600;color:#38bdf8\">Does setting the scheduler to &#8220;none&#8221; risk write starvation during high read bursts?<\/summary>\n<p style=\"margin-top:10px;color:#cbd5e1\">No. Modern enterprise NVMe drives feature sophisticated multi-core ASIC controllers that execute dynamic channel arbitration and round-robin dispatch across internal NAND channels. Because NVMe hardware manages thousands of independent queues, write operations do not get blocked behind read requests at the hardware controller level.<\/p>\n<\/details>\n<details style=\"background:#1e293b;border:1px solid #334155;border-radius:8px;padding:14px;margin-bottom:12px\">\n<summary style=\"cursor:pointer;font-weight:600;color:#38bdf8\">When should I use mq-deadline or Kyber instead of none on NVMe storage?<\/summary>\n<p style=\"margin-top:10px;color:#cbd5e1\"><code>mq-deadline<\/code> or <code>kyber<\/code> can be beneficial on consumer-grade QLC NVMe drives or legacy PCIe Gen3 drives that lack advanced controller queue parallelism. If a drive exhibits thermal throttling or erratic write latency during continuous cache flushes, Kyber&#8217;s latency-target throttling can prevent background writes from degrading interactive read responsiveness.<\/p>\n<\/details>\n<details style=\"background:#1e293b;border:1px solid #334155;border-radius:8px;padding:14px;margin-bottom:12px\">\n<summary style=\"cursor:pointer;font-weight:600;color:#38bdf8\">How does Linux IO scheduler NVMe tuning apply inside virtualized KVM\/QEMU guests?<\/summary>\n<p style=\"margin-top:10px;color:#cbd5e1\">Inside virtual machines using <code>virtio-blk<\/code> or <code>virtio-scsi<\/code>, setting the guest scheduler to <code>none<\/code> is strongly recommended. Applying software elevators inside a virtual guest creates double-queuing overhead, as the host hypervisor already schedules block operations. Allowing the guest to dispatch directly via <code>none<\/code> minimizes hypercall latency and CPU consumption.<\/p>\n<\/details>\n<details style=\"background:#1e293b;border:1px solid #334155;border-radius:8px;padding:14px;margin-bottom:12px\">\n<summary style=\"cursor:pointer;font-weight:600;color:#38bdf8\">Why does rq_affinity=2 produce higher throughput than rq_affinity=1 on multi-core servers?<\/summary>\n<p style=\"margin-top:10px;color:#cbd5e1\">While <code>rq_affinity=1<\/code> allows any CPU core on the same NUMA node to complete the I\/O, this frequently causes thread cache line transfers between separate core caches. In contrast, <code>rq_affinity=2<\/code> enforces complete CPU pin alignment: the exact CPU core that originated the request handles the interrupt callback, eliminating L1\/L2 cache invalidation and reducing CPU context switches.<\/p>\n<\/details>\n<div style=\"background:linear-gradient(135deg, #0f172a 0%, #1e293b 100%);border:1px solid #334155;border-radius:12px;padding:32px;margin:40px 0;text-align:center\">\n<h3 style=\"color:#ffffff;margin-top:0;font-size:22px\">Ready to Deploy High-Performance Infrastructure?<\/h3>\n<p style=\"color:#cbd5e1;font-size:16px;line-height:1.6;max-width:680px;margin:12px auto 24px auto\">Experience blazing-fast NVMe storage, unmetered bandwidth, and enterprise LiteSpeed caching on CpanelFree.<\/p>\n<p><a href=\"https:\/\/cpanelfree.com\" style=\"background:#38bdf8;color:#0f172a;font-weight:700;padding:12px 28px;border-radius:6px;text-decoration:none;display:inline-block;font-size:15px\">Get Started with Free Cloud Hosting &rarr;<\/a><\/div>\n","protected":false},"excerpt":{"rendered":"<p>Optimize Linux block layer I\/O schedulers for high-concurrency NVMe SSDs. Learn when to choose none over mq-deadline to maximize IOPS and slash tail latency.<\/p>\n","protected":false},"author":1,"featured_media":4516,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[169],"tags":[57,87,112,170,101],"class_list":["post-4517","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-performance-tuning","tag-almalinux","tag-devops","tag-performance","tag-performance-tuning","tag-sysadmin"],"_links":{"self":[{"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/posts\/4517","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/comments?post=4517"}],"version-history":[{"count":0,"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/posts\/4517\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/media\/4516"}],"wp:attachment":[{"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/media?parent=4517"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/categories?post=4517"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/tags?post=4517"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}