<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Ceph on Mer's Blog</title><link>https://merisky.top/tags/ceph/</link><description>Recent content in Ceph on Mer's Blog</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Mon, 29 May 2023 11:50:14 +0800</lastBuildDate><atom:link href="https://merisky.top/tags/ceph/index.xml" rel="self" type="application/rss+xml"/><item><title>Ceph-读写流程：客户端写流程分析(二)</title><link>https://merisky.top/posts/867489514/</link><pubDate>Mon, 29 May 2023 03:36:46 +0000</pubDate><guid>https://merisky.top/posts/867489514/</guid><description>&lt;p&gt;本文接上文对 &lt;code&gt;Filer::write_trunc&lt;/code&gt; 及之后的流程做了梳理和总结.&lt;/p&gt;&#10;&lt;h2 id="正文"&gt;&lt;a href="#正文" class="headerlink" title="正文"&gt;&lt;/a&gt;正文&lt;/h2&gt;&lt;p&gt;本文是 &lt;a href="https://merisky.top/posts/748254147/"&gt;Ceph 读写流程：客户端写流程分析&lt;/a&gt; 的后续，前文包括 &lt;code&gt;ll_open&lt;/code&gt; 和 &lt;code&gt;ll_write&lt;/code&gt; 的处理流程。&lt;/p&gt;&#10;&lt;p&gt;Client 通过 &lt;code&gt;Filer::write_trunc&lt;/code&gt; 写文件时会做两件事： 通过 &lt;code&gt;Striper::file_to_extents&lt;/code&gt; 做一次客户端条带化，将要写入的部分转换为对象的 extent 集合；接着通过 &lt;code&gt;objecter-&amp;gt;sg_write_trunc&lt;/code&gt; 进行写入&lt;/p&gt;&#10;&lt;figure class="highlight cpp"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;&lt;span class="function"&gt;&lt;span class="type"&gt;void&lt;/span&gt; &lt;span class="title"&gt;write_trunc&lt;/span&gt; &lt;span class="params"&gt;(...)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt;&lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; vector&amp;lt;ObjectExtent&amp;gt; extents;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; Striper::&lt;span class="built_in"&gt;file_to_extents&lt;/span&gt;(cct, ino, layout, offset, len, truncate_size,&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&#9;&#9;&#9; extents);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; objecter-&amp;gt;&lt;span class="built_in"&gt;sg_write_trunc&lt;/span&gt;(extents, snapc, bl, mtime, flags,&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&#9;&#9; truncate_size, truncate_seq, oncommit, op_flags);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p&gt;而在 &lt;code&gt;sg_write_trunc&lt;/code&gt; 中，objecter 会向每个 &lt;code&gt;ObjectExtent&lt;/code&gt; 对应的 OSD 发送 op 请求，这里通过 &lt;code&gt;write_trunc&lt;/code&gt; 完成，考虑 &lt;code&gt;extents.size() == 1&lt;/code&gt; 的情况如下：&lt;/p&gt;</description></item><item><title>Ceph-Fuse Perms heap-buffer-overflow 问题</title><link>https://merisky.top/posts/3177229983/</link><pubDate>Sun, 23 Apr 2023 08:20:53 +0000</pubDate><guid>https://merisky.top/posts/3177229983/</guid><description>&lt;p&gt;Ceph-fuse 带 Address-Sanitizer 跑发现只要 MDS 切换就会导致客户端 heap-buffer-overflow (ASAN 信息附在最后)&lt;/p&gt;&#10;&lt;h2 id="问题分析"&gt;&lt;a href="#问题分析" class="headerlink" title="问题分析"&gt;&lt;/a&gt;问题分析&lt;/h2&gt;&lt;p&gt;带 debuginfo 的情况下 ASAN 报的信息还是比较全的，说踩内存的地方在 &lt;code&gt;src/messages/MClientRequest.h:151&lt;/code&gt; 的 &lt;code&gt;MClientRequest::set_gid_list&lt;/code&gt; 函数中，并且指出这里尝试读了一个野指针，看下 &lt;code&gt;MClientRequest::set_gid_list&lt;/code&gt; 函数这里的行为：&lt;/p&gt;&#10;&lt;figure class="highlight cpp"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;&#9;&lt;span class="function"&gt;&lt;span class="type"&gt;void&lt;/span&gt; &lt;span class="title"&gt;set_gid_list&lt;/span&gt;&lt;span class="params"&gt;(&lt;span class="type"&gt;int&lt;/span&gt; count, &lt;span class="type"&gt;const&lt;/span&gt; &lt;span class="type"&gt;gid_t&lt;/span&gt; *gids)&lt;/span&gt; &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&#9;&#9;gid_list.&lt;span class="built_in"&gt;reserve&lt;/span&gt;(count);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&#9;&#9;&lt;span class="keyword"&gt;for&lt;/span&gt; (&lt;span class="type"&gt;int&lt;/span&gt; i = &lt;span class="number"&gt;0&lt;/span&gt;; i &amp;lt; count; ++i) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;→ gid_list.&lt;span class="built_in"&gt;push_back&lt;/span&gt;(gids[i]);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&#9;&#9;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&#9;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p&gt;可以看到这里问题就在 &lt;code&gt;gids&lt;/code&gt; 上，看一下这个指针是哪里传进来的：&lt;/p&gt;</description></item><item><title>Ceph MDS InodeTable 分配流程和故障分析</title><link>https://merisky.top/posts/3495093285/</link><pubDate>Fri, 14 Apr 2023 00:33:43 +0000</pubDate><guid>https://merisky.top/posts/3495093285/</guid><description>&lt;p&gt;本文对 Ceph 中 &lt;code&gt;inode&lt;/code&gt; 分配流程进行了总结，并解释了 MDS 重启过程中可能遇到的一个故障&lt;/p&gt;&#10;&lt;h2 id="inode-分配流程"&gt;&lt;a href="#inode-分配流程" class="headerlink" title="inode 分配流程"&gt;&lt;/a&gt;inode 分配流程&lt;/h2&gt;&lt;p&gt;在 Ceph 中我们创建目录、文件、软链接时都需要为其分配一个 &lt;code&gt;inode&lt;/code&gt; 作为唯一标识，这一分配过程在 &lt;code&gt;Server::prepare_new_inode&lt;/code&gt; 中完成，而分配的依据，也就是哪些 &lt;code&gt;inode&lt;/code&gt; 还没有被分配，哪些&lt;br&gt;被分配了还没有生效（落盘）则是记录在 &lt;code&gt;InoTable&lt;/code&gt; 中:&lt;/p&gt;&#10;&lt;figure class="highlight cpp"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;&lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="title class_"&gt;InoTable&lt;/span&gt; : &lt;span class="keyword"&gt;public&lt;/span&gt; MDSTable &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;...&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="keyword"&gt;private&lt;/span&gt;:&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; interval_set&amp;lt;&lt;span class="type"&gt;inodeno_t&lt;/span&gt;&amp;gt; free; &lt;span class="comment"&gt;// unused ids&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; interval_set&amp;lt;&lt;span class="type"&gt;inodeno_t&lt;/span&gt;&amp;gt; projected_free;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;...&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p&gt;其中 &lt;code&gt;free&lt;/code&gt; 记录的就是没有被分配的 &lt;code&gt;inode&lt;/code&gt; 集合，而 &lt;code&gt;projected_free&lt;/code&gt; 则是从 &lt;code&gt;free&lt;/code&gt; 中剔除了已经被分配但是还没有落盘的部分，因此 &lt;code&gt;projected_free&lt;/code&gt; 是 &lt;code&gt;free&lt;/code&gt; 的子集，同时显然分配也首先要在 &lt;code&gt;projected_free&lt;/code&gt; 中来做。&lt;/p&gt;</description></item><item><title>Ceph MDS MetaBlob 分析</title><link>https://merisky.top/posts/3797827770/</link><pubDate>Thu, 06 Apr 2023 07:28:29 +0000</pubDate><guid>https://merisky.top/posts/3797827770/</guid><description>&lt;p&gt;CephFS 中会把多种事件记录在不同类型的 &lt;code&gt;LogEvent&lt;/code&gt; 中，其中最常见的也是最重要的就是读写流程中处理客户端请求时记录的 &lt;code&gt;EUpdate&lt;/code&gt; 事件，而在 &lt;code&gt;EUpdate&lt;/code&gt; 中又会把元数据的状态保存在 &lt;code&gt;MetaBlob&lt;/code&gt; 结构中，本文就主要对 &lt;code&gt;MetaBlob&lt;/code&gt; 结构以及其中保存的内容做一个总结。&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;MetaBlob&lt;/code&gt; 类定义看上去比较复杂，实际最重要的成员只有三个：&lt;/p&gt;&#10;&lt;figure class="highlight cpp"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;&lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="title class_"&gt;EMetaBlob&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="keyword"&gt;public&lt;/span&gt;:&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// my lumps. preserve the order we added them in a list.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::vector&amp;lt;&lt;span class="type"&gt;dirfrag_t&lt;/span&gt;&amp;gt; lump_order;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::map&amp;lt;&lt;span class="type"&gt;dirfrag_t&lt;/span&gt;, dirlump&amp;gt; lump_map;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::list&amp;lt;fullbit&amp;gt; roots;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p&gt;其中 &lt;code&gt;lump_order&lt;/code&gt; 保存从根目录到父母路的 Dir 分片信息 &lt;code&gt;dirfrag_t&lt;/code&gt;，&lt;code&gt;lump_map&lt;/code&gt; 中保存 &lt;code&gt;dirfrag_t&lt;/code&gt; 到 &lt;code&gt;dirlump&lt;/code&gt; 的映射关系， &lt;code&gt;dirlump&lt;/code&gt; 中保存的就是相关的元数据信息，最后 &lt;code&gt;roots&lt;/code&gt; 保存的就是 root 节点的 &lt;code&gt;fullbit&lt;/code&gt; 信息，&lt;code&gt;fullbit&lt;/code&gt; 表示一个存在的 &lt;code&gt;dn + inode&lt;/code&gt;，相对的 &lt;code&gt;MetaBlob&lt;/code&gt; 中使用 &lt;code&gt;null_bit&lt;/code&gt; 结构保存一个不存在的 &lt;code&gt;dentry&lt;/code&gt;&lt;/p&gt;</description></item><item><title>Paxos 算法以及 Ceph 实现</title><link>https://merisky.top/posts/3640854188/</link><pubDate>Wed, 05 Apr 2023 14:57:58 +0000</pubDate><guid>https://merisky.top/posts/3640854188/</guid><description>&lt;p&gt;本文包含 Paxos、Multi-Paxos 算法的原理以及 Ceph 中对 Paxos 算法的实现。&lt;/p&gt;&#10;&lt;h2 id="Basic-Paxos-和-Multi-Paxos"&gt;&lt;a href="#Basic-Paxos-和-Multi-Paxos" class="headerlink" title="Basic Paxos 和 Multi-Paxos"&gt;&lt;/a&gt;Basic Paxos 和 Multi-Paxos&lt;/h2&gt;&lt;p&gt;Paxos 和 raft 算法看过不少次，但是始终属于一知半解，也没有个记录，这次把 Paxos 和 Multi-Paxos 的原理和 ceph 中 Paxos 的实现都记录一下：&lt;/p&gt;&#10;&lt;p&gt;首先是 Paxos 算法，朴素的 Paxos 算法是不包含 leader 角色的：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;proposer (提议者): 提出提案，包含提案编号（proposal ID）和一个要协商的值（Val）&lt;/li&gt;&#10;&lt;li&gt;acceptor (决策者)：参与决策，回应收到的来自 proposer 的提案，当多数 acceptor 赞成时提案通过，一般来说一个 proposer 同时也是一个 acceptor&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Basic Paxos 算法通过两阶段通过一个决议：&lt;/p&gt;</description></item><item><title>Ceph 读写流程：客户端写流程分析</title><link>https://merisky.top/posts/748254147/</link><pubDate>Sat, 01 Apr 2023 10:01:16 +0000</pubDate><guid>https://merisky.top/posts/748254147/</guid><description>&lt;p&gt;本文对 Ceph 客户端写流程进行了梳理和总结，包括 &lt;code&gt;ll_open&lt;/code&gt; 和 &lt;code&gt;ll_write&lt;/code&gt; 的处理流程。&lt;/p&gt;&#10;&lt;h2 id="正文"&gt;&lt;a href="#正文" class="headerlink" title="正文"&gt;&lt;/a&gt;正文&lt;/h2&gt;&lt;p&gt;首先用户需要通过 &lt;code&gt;ll_open&lt;/code&gt; 打开文件并拿到 fh，第一步先做权限检查，进入 &lt;code&gt;may_open&lt;/code&gt;, 这里会根据用户带回的 flags 确定 &lt;code&gt;want&lt;/code&gt; 是 &lt;code&gt;MAY_WRITE&lt;/code&gt; 还是 &lt;code&gt;MAY_READ&lt;/code&gt;, 接着通过一次 getattr 拿到文件的 mode, 当然这里 getattr 不是每次都发，只有 client 认为自己的所持有的文件权限不是最新的时候才会发，具体有两种情况：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;开启 acl 并且没有拿过文件的 xattr (acl 设置是外带在 xattr 中的，不在 inode 里)&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;caps_issued_mask&lt;/code&gt; 认为当前 client 的 caps 不足以保证所持有的权限为最新时&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;details class="tag-plugin folding" color="yellow" child="codeblock"&gt;&lt;summary&gt;&lt;span&gt;Client::_getattr 判断是否需要发送请求&lt;/span&gt;&lt;/summary&gt;&lt;div class="body"&gt;&lt;figure class="highlight cpp"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;18&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;&lt;span class="type"&gt;int&lt;/span&gt; Client::_getattr(Inode *in, &lt;span class="type"&gt;int&lt;/span&gt; mask, &lt;span class="type"&gt;const&lt;/span&gt; UserPerm&amp;amp; perms, &lt;span class="type"&gt;bool&lt;/span&gt; force)&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="type"&gt;bool&lt;/span&gt; yes = in-&amp;gt;&lt;span class="built_in"&gt;caps_issued_mask&lt;/span&gt;(mask, &lt;span class="literal"&gt;true&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; (yes &amp;amp;&amp;amp; !force)&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;...&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// force 和 mask:&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="type"&gt;int&lt;/span&gt; Client::_getattr_for_perm(Inode *in, &lt;span class="type"&gt;const&lt;/span&gt; UserPerm&amp;amp; perms)&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="type"&gt;int&lt;/span&gt; mask = CEPH_STAT_CAP_MODE;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="type"&gt;bool&lt;/span&gt; force = &lt;span class="literal"&gt;false&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; (acl_type != NO_ACL) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; mask |= CEPH_STAT_CAP_XATTR;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; force = in-&amp;gt;xattr_version == &lt;span class="number"&gt;0&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;return&lt;/span&gt; _getattr(in, mask, perms, force);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/details&gt;&#10;&lt;p&gt;简单解释一下第二点就是如果 client 没有持有 As （开启 acl 的话还需要 Xs）那么就需要向 mds 发 getattr&lt;br&gt;拿到 perm 之后做一个 &lt;code&gt;inode_permission&lt;/code&gt; 检查一下 mode (如果设了 acl 那就检查 acl)&lt;/p&gt;</description></item><item><title>Ceph 读写流程：file_to_extents 过程分析</title><link>https://merisky.top/posts/2816966616/</link><pubDate>Thu, 30 Mar 2023 06:02:08 +0000</pubDate><guid>https://merisky.top/posts/2816966616/</guid><description>&lt;p&gt;本文介绍 Ceph 中条带化的相关概念并对 &lt;code&gt;Striper::file_to_extents&lt;/code&gt; 的流程进行了分析和总结。&lt;/p&gt;&#10;&lt;p&gt;在 &lt;code&gt;Client::_write&lt;/code&gt; 中客户端通过 &lt;code&gt;filer-&amp;gt;write_trunc&lt;/code&gt; 对文件进行写入，&lt;code&gt;write_trunc&lt;/code&gt; 其中分为两步:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;通过 &lt;code&gt;Striper::file_to_extents&lt;/code&gt; 对请求进行拆分，也就是将对文件的读写请求转换为对对象的读写请求&lt;/li&gt;&#10;&lt;li&gt;通过 &lt;code&gt;objecter-&amp;gt;sg_write_trunc&lt;/code&gt; 把数据写到 object 中&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;本文主要对其中的第一步，也就是 &lt;code&gt;Striper::file_to_extents&lt;/code&gt; 的过程进行分析。&lt;/p&gt;&#10;&lt;h2 id="Ceph-中的-Stripping"&gt;&lt;a href="#Ceph-中的-Stripping" class="headerlink" title="Ceph 中的 Stripping"&gt;&lt;/a&gt;Ceph 中的 Stripping&lt;/h2&gt;&lt;p&gt;在正式开始先简单介绍一下 Ceph 中条带化（Stripping）读写的相关概念：&lt;/p&gt;&#10;&lt;details class="tag-plugin folding" &gt;&lt;summary&gt;&lt;span&gt;为什么要使用 Stripping&lt;/span&gt;&lt;/summary&gt;&lt;div class="body"&gt;&lt;p&gt;最主要的原因就是由于单个存储设备存在吞吐量限制，例如一块机械硬盘的写入能力最大也不过 200MB&amp;#x2F;s, 在与内存、固态缓存甚至是万兆网络的吞吐量对比下无疑就是整个系统中最大的短板，那么条带化就是用来解决存储设备性能瓶颈，提高存储系统吞吐量的东西。&lt;/p&gt;&lt;p&gt;那么什么是条带化，说的准确一点就是把信息的连续部分存储在多个存储设备上，说人话就是把一个文件切成多份放在不同的磁盘上，每次对文件中一段内容的读写会同时发给多个盘，这样的话我们实际的写入速度就变成了多个磁盘的写入速度总和。&lt;/p&gt;&lt;/div&gt;&lt;/details&gt;&#10;&lt;p&gt;Ceph 中的条带化的概念大致和其他存储没有区别，只不过在 Ceph 中所有的存储最后都会落到 &lt;code&gt;object&lt;/code&gt; 也就是对象上，而对象的存储是没有条带化的，因此文件的条带化实际上是在客户端上进行的，也就是我们本文的主要内容 &lt;code&gt;file_to_extents&lt;/code&gt; 的过程&lt;/p&gt;</description></item><item><title>Ceph 挂载点压缩提示 File changed as we read it 问题分析</title><link>https://merisky.top/posts/3535257457/</link><pubDate>Mon, 27 Mar 2023 06:50:07 +0000</pubDate><guid>https://merisky.top/posts/3535257457/</guid><description>&lt;p&gt;使用 CephFS 的同学可能会发现如果在 CephFS 挂载点下去压缩文件的时候有时候会提示 &lt;code&gt;File changed as we read it&lt;/code&gt;，有时候还会导致&lt;br&gt;CRC 失败，本文对 &lt;code&gt;File changed as we read it&lt;/code&gt; 问题出现的原因进行了分析并给出了解决办法，希望能对大家有所帮助。&lt;/p&gt;&#10;&lt;h2 id="问题现象"&gt;&lt;a href="#问题现象" class="headerlink" title="问题现象"&gt;&lt;/a&gt;问题现象&lt;/h2&gt;&lt;p&gt;首先这个问题可能导致的现象比较多样，最基础的就是把一个压缩文件解压一下然后立马压缩，这时就会提示 &lt;code&gt;File changed as we read it&lt;/code&gt; 如下：&lt;/p&gt;&#10;&lt;figure class="highlight bash"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;[root@An1Uit ceph-fuse]&lt;span class="comment"&gt;# ./tar.sh&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;tar: cmake-3.7.2/Tests/LinkFlags: 在我们读入文件时文件发生了变化&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;tar: cmake-3.7.2/Tests/SimpleCOnly: 在我们读入文件时文件发生了变化&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;tar: cmake-3.7.2/Tests/ObjC++: 在我们读入文件时文件发生了变化&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p&gt;那除了这个现象以外还可能导致一些应用的 CRC 校验失败，或者 nfs 的 REMOTE I&amp;#x2F;O ERROR&lt;/p&gt;</description></item><item><title>Ceph MDS Stuck in Client Replay 问题分析</title><link>https://merisky.top/posts/2094044811/</link><pubDate>Sun, 26 Mar 2023 04:34:54 +0000</pubDate><guid>https://merisky.top/posts/2094044811/</guid><description>&lt;p&gt;最近一直在做 MDS 高可用方面的工作，发现 MDS （带 IO）重启时可能会长时间卡在 Client Replay 状态。这里对问题的原因做了一下分析，并给出了现有的以及未来的解决办法，希望能对大家有所帮助。&lt;/p&gt;&#10;&lt;h2 id="问题现象梳理"&gt;&lt;a href="#问题现象梳理" class="headerlink" title="问题现象梳理"&gt;&lt;/a&gt;问题现象梳理&lt;/h2&gt;&lt;p&gt;通过 &lt;code&gt;ceph -s&lt;/code&gt; 看到 MDS 的状态长时间在 &lt;code&gt;client replay&lt;/code&gt; 状态不变化：&lt;/p&gt;&#10;&lt;figure class="highlight bash"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;[root@node2 ~]&lt;span class="comment"&gt;# ceph -s&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; cluster:&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; ...&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; mds: cephfs: 1/1 &amp;#123;0=node2=up:clientreplay&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p&gt;查看 MDS 状态会发现 &lt;code&gt;clientreplay_queue&lt;/code&gt; 不为空，但是如果打开 MDS 日志会发现 MDS 什么都没做（除了心跳）：&lt;/p&gt;</description></item><item><title>Ceph Client 中的 LRU 设计</title><link>https://merisky.top/posts/3763995770/</link><pubDate>Wed, 08 Mar 2023 13:38:20 +0000</pubDate><guid>https://merisky.top/posts/3763995770/</guid><description>&lt;p&gt;在 Ceph 中 Client 和 MDS 部分的 lru 实现和淘汰策略稍有不同，但作用都是用来管理 dentry 等结构，方便及时将不使用的文件和目录清出内存，这里主要关注 lru 的实现而不关注 dentry 的淘汰时机。&lt;/p&gt;&#10;&lt;p&gt;首先看下 LRU 的结构：&lt;/p&gt;&#10;&lt;figure class="highlight cpp"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// lru.h&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="title class_"&gt;LRU&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="keyword"&gt;private&lt;/span&gt;:&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;using&lt;/span&gt; LRUList = xlist&amp;lt;LRUObject*&amp;gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; LRUList top, bottom, pintail;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p&gt;这里和大部分 LRU 的实现类似， ceph 也使用一个双向链表作为 lru 的基础结构（也就是这里的 xlist，基本可以当作一个双向链表看待）。只不过 ceph 同时使用了三个链表 top、bottom 和 pintail 来细化 lru 的不同位置。&lt;/p&gt;</description></item></channel></rss>