<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Enodev.fr / Christophe's log (Posts about RAM)</title><link>https://www.enodev.fr/</link><description></description><atom:link href="https://www.enodev.fr/categories/ram.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><lastBuildDate>Sun, 10 Mar 2024 10:25:04 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Emulate a slow block device with dm-delay</title><link>https://www.enodev.fr/posts/emulate-a-slow-block-device-with-dm-delay.html</link><dc:creator>Christophe Vu-Brugier</dc:creator><description>&lt;p&gt;When debugging a problem caused by high I/O latency on Linux, it may
be interesting to emulate a slow or congested block device. The device
mapper driver which manages logical volumes on Linux has a solution
for that: the &lt;em&gt;dm-delay&lt;/em&gt; target.&lt;/p&gt;
&lt;p&gt;In this article, we will use the &lt;em&gt;dm-delay&lt;/em&gt; target to delay reads and
writes to a block device. We will first create a ramdisk which is an
extremely fast block device. Then, we will stack the &lt;em&gt;dm-delay&lt;/em&gt; target
on top of it and measure the I/O latency it introduces.&lt;/p&gt;
&lt;h2&gt;Creating a ramdisk&lt;/h2&gt;
&lt;p&gt;A ramdisk is a RAM backed disk. Since data written to RAM do not
persist without power, do not store real data on a ramdisk. Compared
to a hard disk drive, a ramdisk is much smaller in size: its size
cannot exceed the computer RAM size. But a ramdisk is much faster than
a hard disk drive.&lt;/p&gt;
&lt;p&gt;On Linux, loading the
&lt;a href="https://elixir.bootlin.com/linux/latest/source/drivers/block/brd.c"&gt;brd&lt;/a&gt;
kernel module creates a set of ramdisks. Passing arguments to the
&lt;code&gt;modprobe&lt;/code&gt; command configures the number of ramdisks and their size:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;rd_nr&lt;/code&gt; sets the maximum number of ramdisks.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rd_size&lt;/code&gt; sets the size of each ramdisk in &lt;abbr title="kibibytes"&gt;KiB&lt;/abbr&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The command below creates a 1 GB ramdisk:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;modprobe&lt;span class="w"&gt; &lt;/span&gt;brd&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;rd_nr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;rd_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1048576&lt;/span&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;-l&lt;span class="w"&gt; &lt;/span&gt;/dev/ram0
&lt;span class="go"&gt;brw-rw---- 1 root disk 1, 0 Aug 24 20:00 /dev/ram0&lt;/span&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;blockdev&lt;span class="w"&gt; &lt;/span&gt;--getsize&lt;span class="w"&gt; &lt;/span&gt;/dev/ram0&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# Display the size in 512-byte sectors&lt;/span&gt;
&lt;span class="go"&gt;2097152&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Creating a delayed target with dm-delay&lt;/h2&gt;
&lt;p&gt;The
&lt;a href="https://www.kernel.org/doc/html/latest/admin-guide/device-mapper/delay.html"&gt;kernel documentation&lt;/a&gt;
explains how to configure a delayed target with &lt;code&gt;dmsetup&lt;/code&gt;. For
instance, you can use a script like this one to stack a delayed block
device on top of a given block device:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="ch"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="c1"&gt;# Create a block device that delays reads for 500 ms&lt;/span&gt;
&lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;blockdev&lt;span class="w"&gt; &lt;/span&gt;--getsize&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# Size in 512-bytes sectors&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0 &lt;/span&gt;&lt;span class="nv"&gt;$size&lt;/span&gt;&lt;span class="s2"&gt; delay &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt; 0 500"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dmsetup&lt;span class="w"&gt; &lt;/span&gt;create&lt;span class="w"&gt; &lt;/span&gt;delayed
&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Checking the latency of dm-delay&lt;/h2&gt;
&lt;p&gt;Let's check the latency introduced by &lt;em&gt;dm-delay&lt;/em&gt;. We use
&lt;a href="https://github.com/axboe/fio"&gt;fio&lt;/a&gt; to compare the latency of the
ramdisk (&lt;code&gt;/dev/ram0&lt;/code&gt;) with the latency of the delayed device
(&lt;code&gt;/dev/dm-0&lt;/code&gt;). The job file for fio that describes the I/O workload is
as follows:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;[random]&lt;/span&gt;
&lt;span class="c1"&gt;# Perform 4K random reads for 10 seconds using direct I/Os&lt;/span&gt;
&lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/dev/dm-0&lt;/span&gt;
&lt;span class="na"&gt;readwrite&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;randread&lt;/span&gt;
&lt;span class="na"&gt;blocksize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;4k&lt;/span&gt;
&lt;span class="na"&gt;ioengine&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;sync&lt;/span&gt;
&lt;span class="na"&gt;direct&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;time_based&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;10&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;At the end of the run, fio displays a bunch of statistics. One of them
is the &lt;em&gt;completion latency&lt;/em&gt; (denoted as &lt;code&gt;clat&lt;/code&gt;):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ramdisk: 1.33 µs&lt;/li&gt;
&lt;li&gt;delayed block device: 499735.14 µs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The latency of the ramdisk is a few microseconds whereas the latency
of the delayed block device backed by the ramdisk is close to the
500 ms delay we requested.&lt;/p&gt;
&lt;p&gt;A similar experiment for writes shows that &lt;code&gt;dm-delay&lt;/code&gt; also delays
writes to the device. To delay writes with &lt;code&gt;dm-delay&lt;/code&gt;, give a second
set of parameters to &lt;code&gt;dmsetup&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="ch"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="c1"&gt;# Create a block device that delays reads for 500 ms and writes for 300 ms&lt;/span&gt;
&lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;blockdev&lt;span class="w"&gt; &lt;/span&gt;--getsize&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# Size in 512-bytes sectors&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0 &lt;/span&gt;&lt;span class="nv"&gt;$size&lt;/span&gt;&lt;span class="s2"&gt; delay &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt; 0 500 &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt; 0 300"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dmsetup&lt;span class="w"&gt; &lt;/span&gt;create&lt;span class="w"&gt; &lt;/span&gt;delayed
&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Suspending I/Os&lt;/h2&gt;
&lt;p&gt;The device mapper can also be requested to suspend and resume I/Os.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;dmsetup&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;suspend&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/dev/dm-0
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;dmsetup&lt;span class="w"&gt; &lt;/span&gt;resume&lt;span class="w"&gt;  &lt;/span&gt;/dev/dm-0
&lt;/pre&gt;&lt;/div&gt;</description><category>device mapper</category><category>Linux</category><category>RAM</category><category>storage</category><category>testing</category><guid>https://www.enodev.fr/posts/emulate-a-slow-block-device-with-dm-delay.html</guid><pubDate>Tue, 25 Aug 2015 18:00:00 GMT</pubDate></item></channel></rss>