<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title><![CDATA[Ant-Veil]]></title>
  <link href="http://www.casparzhang.com/atom.xml" rel="self"/>
  <link href="http://www.casparzhang.com/"/>
  <updated>2017-01-10T11:04:38+08:00</updated>
  <id>http://www.casparzhang.com/</id>
  <author>
    <name><![CDATA[Caspar]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[Analyse Kernel Structures With Crash Utility]]></title>
    <link href="http://www.casparzhang.com/blog/2017/01/10/analyse-kernel-structures-with-crash-utility/"/>
    <updated>2017-01-10T00:35:59+08:00</updated>
    <id>http://www.casparzhang.com/blog/2017/01/10/analyse-kernel-structures-with-crash-utility</id>
    <content type="html"><![CDATA[<p>关于 Kdump 和 Crash 的背景知识，不是本文的重点，可以参考下列文章：</p>

<ul>
<li><a href="http://www.ibm.com/developerworks/cn/views/linux/libraryview.jsp?search_by=%E6%B7%B1%E5%85%A5%E6%8E%A2%E7%B4%A2+Kdump">developerworks 社区深入探索 Kdump 系列文档</a></li>
<li><a href="https://people.redhat.com/anderson/crash_whitepaper/">Dave Anderson Crash 官方文档</a></li>
</ul>


<p>这次碰到的问题是，系统当时死锁了，抓了个 vmcore 来研究，现在要看层层包含的某个结构体的一个 <code>rw_semaphore</code> 结构体变量的信息。当时这一层层的基本结构大体是这样的：</p>

<p>有两个链表，分别叫做: <code>sets</code> 和 <code>set_devs</code>; 顾名思义，第一个 <code>sets</code> 是一堆 <code>set</code> 结构体组成的链表；第二个 <code>set_devs</code> 是一堆 <code>set_dev</code> 结构体组成的链表。两个结构体如下：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>struct set {
</span><span class='line'>    struct list_head *list;
</span><span class='line'>    blah blah blah;
</span><span class='line'>    struct list_head *set_devs;
</span><span class='line'>};
</span><span class='line'>
</span><span class='line'>struct set_dev {
</span><span class='line'>    struct list_head *list;
</span><span class='line'>    blah blah blah;
</span><span class='line'>    struct rw_semaphore rw_sem;
</span><span class='line'>};</span></code></pre></td></tr></table></div></figure>


<p>最终任务是要找到 <code>set_devs</code> 这个列表里所有 <code>set_dev</code> 的 <code>lock</code> 这个信号量成员变量的信息。</p>

<p>首先，打开 vmcore (确保 kernel debuginfo 都在)：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>sudo crash /usr/lib/debug/lib/modules/3.10.0/vmlinux /var/crash/vmcore</span></code></pre></td></tr></table></div></figure>


<p>如果要分析的结构体在内核模块中，还需要用 <code>mod -s/-S</code> 来加载内核模块的 debuginfo.</p>

<p>然后找到 <code>sets</code> 这个链表的入口地址。很幸运，模块里这个链表是全局的，直接找到 symbol 就可以用：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>crash&gt; p sets
</span><span class='line'>sets = $1 = {
</span><span class='line'>  next = 0xffffc90056696030,
</span><span class='line'>  prev = 0xffffc90056696030
</span><span class='line'>}
</span><span class='line'>crash&gt; p &sets // 顺手确认一下链表是否为空，如果 &sets == sets-&gt;next 就为空
</span><span class='line'>$2 = (struct list_head *) 0xffffffffa04401f0 &lt;sets&gt;</span></code></pre></td></tr></table></div></figure>


<p>我们就得到了一个不为空的链表的第一个元素（从上面也可以看到是唯一的一个）。由于内核里的链表只有指针没有元素内容，我们需要用类似 <code>container_of</code> 的机制来从成员反查链表元素的地址：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>crash&gt; set -l set.list 0xffffc90056696030
</span><span class='line'>struct set {
</span><span class='line'>  &lt;snip&gt;
</span><span class='line'>  list = {
</span><span class='line'>    next = 0xffffffffa04401f0 &lt;sets&gt;,
</span><span class='line'>    prev = 0xffffffffa04401f0 &lt;sets&gt;
</span><span class='line'>  },
</span><span class='line'>  &lt;snip&gt;
</span><span class='line'>  set_devs = {
</span><span class='line'>    next = 0xffffc90050f67000,
</span><span class='line'>    prev = 0xffffc90056ee4000
</span><span class='line'>  },    
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p>这样我们又得到了一个链表的 LIST_HEAD 地址：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>crash&gt; list_head 0xffffc90050f67000
</span><span class='line'>struct list_head {
</span><span class='line'>  next = 0xffffc90065f2a000,
</span><span class='line'>  prev = 0xffffc90056696da8
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p>得到第一个元素地址后反查 <code>set_dev</code> 结构体：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>crash&gt; struct set_dev -l set_dev.list 0xffffc90065f2a000
</span><span class='line'>struct set_dev {
</span><span class='line'>  list = {
</span><span class='line'>    next = 0xffffc900583dc000,
</span><span class='line'>    prev = 0xffffc90050f67000
</span><span class='line'>  },
</span><span class='line'>  &lt;snip&gt;
</span><span class='line'>  rw_sem = {
</span><span class='line'>    count = 0,
</span><span class='line'>    wait_lock = {
</span><span class='line'>      raw_lock = {
</span><span class='line'>        {
</span><span class='line'>          head_tail = 0,
</span><span class='line'>          tickets = {
</span><span class='line'>            head = 0,
</span><span class='line'>            tail = 0
</span><span class='line'>          }
</span><span class='line'>        }
</span><span class='line'>      }
</span><span class='line'>    },
</span><span class='line'>    wait_list = {
</span><span class='line'>      next = 0xffffc90065f2ab48,
</span><span class='line'>      prev = 0xffffc90065f2ab48
</span><span class='line'>    }
</span><span class='line'>  },
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p>找到 <code>rw_sem</code> 结构体信息，这正是我们想要的。然后依次往下找即可。btw, 我忘记链表是循环链表了，所以我傻乎乎地手工查了半个小时最后才发现自己重复了……</p>

<p>所以问题来了，crash 里面有快捷的方法可以挨个链表元素查找么……</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Block Device Plugging]]></title>
    <link href="http://www.casparzhang.com/blog/2015/09/10/block-device-plugging/"/>
    <updated>2015-09-10T18:31:25+08:00</updated>
    <id>http://www.casparzhang.com/blog/2015/09/10/block-device-plugging</id>
    <content type="html"><![CDATA[<p>关于 block 设备的 plug 和 unplug 机制，可以参考<a href="http://lxr.free-electrons.com/source/Documentation/block/biodoc.txt#L1011">这篇</a>内核文档。简单来说就是内核提供了一个 plug 机制，请求被放到一个空队列里之后，可以将队列处于 &ldquo;plugged&rdquo; 状态，此时队列并不真正向下层设备发射(issue)请求，而是攒够足够多的请求之后，将队列 &ldquo;unplug&rdquo; 之后才发射，在这期间，I/O 的调度算法将有机会对请求进行合并和排序。unplug 的时机既可以是一个手工的 <code>blk_unplug()</code> 操作，也可以由一个 <code>unplug_timer</code> 定时器超时触发。这整个过程听起来有点像在等机场大巴，到站停靠(plug)，装载乘客(攒 request)，到点发车(unplug_timeout)或者人满发车(blk_unplug)。</p>

<p>很容易想到，unplug_timer 这个定时器的启动会发生在 plug 阶段(见 block/blk-core.c 中的实现):</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>void blk_plug_device(struct request_queue *q)
</span><span class='line'>{
</span><span class='line'>        WARN_ON(!irqs_disabled());
</span><span class='line'>        /*
</span><span class='line'>         * don't plug a stopped queue, it must be paired with blk_start_queue()
</span><span class='line'>         * which will restart the queueing
</span><span class='line'>         */
</span><span class='line'>        if (blk_queue_stopped(q))
</span><span class='line'>                return;
</span><span class='line'>        if (!queue_flag_test_and_set(QUEUE_FLAG_PLUGGED, q)) {
</span><span class='line'>                mod_timer(&q-&gt;unplug_timer, jiffies + q-&gt;unplug_delay);
</span><span class='line'>                trace_block_plug(q);
</span><span class='line'>        }
</span><span class='line'>}
</span><span class='line'>EXPORT_SYMBOL(blk_plug_device);</span></code></pre></td></tr></table></div></figure>


<p>而定时器超时函数见同一个文件下的实现：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>void blk_unplug_timeout(unsigned long data)
</span><span class='line'>{
</span><span class='line'>        struct request_queue *q = (struct request_queue *)data;
</span><span class='line'>        trace_block_unplug_timer(q);
</span><span class='line'>        kblockd_schedule_work(q, &q-&gt;unplug_work);
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p>这个函数是在 <code>blk_queue_make_request</code> 定义的时候设置的(见 block/blk-settings.c)：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>void blk_queue_make_request(struct request_queue *q, make_request_fn *mfn)
</span><span class='line'>{
</span><span class='line'>        &lt;snip&gt;
</span><span class='line'>        q-&gt;unplug_thresh = 4;           /* hmm */
</span><span class='line'>        q-&gt;unplug_delay = msecs_to_jiffies(3);  /* 3 milliseconds */
</span><span class='line'>        if (q-&gt;unplug_delay == 0)
</span><span class='line'>                q-&gt;unplug_delay = 1;
</span><span class='line'>        q-&gt;unplug_timer.function = blk_unplug_timeout;
</span><span class='line'>        q-&gt;unplug_timer.data = (unsigned long)q;
</span><span class='line'>        &lt;snip&gt;
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p></p>

<p>可以看到除了定义了超时函数，还定义了超时的时间，是 3ms. 这个 3ms 可以用来排查一系列奇怪的 I/O 性能问题。比如说在 iodepth 为 1 的情况下，发现 IOPS 是很固定的 333，那么多半就是因为请求队列没有及时 unplug 而导致定时器超时自动 unplug 了。通过简单计算得知，因为每个队列需要 3ms 传输，那一秒钟只能传送 333 个队列，即 IOPS == 333.</p>

<p>如果要主动 unplug，就需要调用 <code>blk_unplug()</code> 函数，或者 <code>generic_unplug_device()</code> 函数，从通用性来说，调用前者会比较好，看如下代码(block/blk-core.c):</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>void blk_unplug(struct request_queue *q)
</span><span class='line'>{
</span><span class='line'>        /*
</span><span class='line'>         * devices don't necessarily have an -&gt;unplug_fn defined
</span><span class='line'>         */
</span><span class='line'>        if (q-&gt;unplug_fn) {
</span><span class='line'>                trace_block_unplug_io(q);
</span><span class='line'>                q-&gt;unplug_fn(q);
</span><span class='line'>        }
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p> <code>q-&gt;unplug_fn</code> 一般不需要自行设置，如果没有定义，会使用默认的函数，即 <code>generic_unplug_device()</code>。</p>

<p>看完了这几个函数，可以知道，plug 完之后及时 unplug 通常是避免 IO 延迟过高的良好手段。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Use Non-export Symbol]]></title>
    <link href="http://www.casparzhang.com/blog/2015/05/21/use-non-export-symbol/"/>
    <updated>2015-05-21T18:38:25+08:00</updated>
    <id>http://www.casparzhang.com/blog/2015/05/21/use-non-export-symbol</id>
    <content type="html"><![CDATA[<p>最近在折腾 <a href="http://linux-iscsi.org">LIO</a>，里面的模块好多，有核心模块(<code>target_core_mod</code>)、前端驱动模块(<code>vhost-scsi</code>, <code>iscsi</code>, etc)、后端驱动模块(<code>target_core_iblock</code>, <code>target_core_file</code>, etc)。我想要实现某种程度的模块“热升级”，具体来说，比如<code>target_core_mod</code>这个核心模块升级了之后，想要和旧模块共存，新的前端和后端驱动在接入的时候直接使用新模块。</p>

<p>于是想当然地认为只要把编译出来的模块改个名字就好了。然而事情并没那么简单，中间碰到两个问题，首先就是本文要讲到的，导出符号(EXPORT_SYMBOL)重复的问题。其实问题很好理解，我改了个名字之后的模块(比如：<code>target_core_mod_new.ko</code>，在代码中还是会导出相同的符号，所以解决方法就是在新的模块中不导出这些符号，删掉所有的<code>EXPORT_SYMBOL()</code>宏。</p>

<p>可是问题又来了，我的后端驱动模块(比如：<code>target_core_iblock.ko</code>)需要依赖新的模块的函数，在无法导出符号的情况下，我没有办法使用后端驱动模块。稍微搜了一下，找到<code>kallsyms_lookup_name()</code>函数(<a href="http://lxr.free-electrons.com/source/kernel/kallsyms.c?v=2.6.33#L170">定义</a>)，这个函数传入一个类似 <strong><em>module:symbol</em></strong> 形式的参数，用于读取<code>/proc/kallsyms</code>中的符号表，找到对应的符号，然后返回符号的地址(即函数的入口)。</p>

<p>于是我拿了这个函数开开心心地去改代码了。以<code>target_complete_cmd()</code>这个函数为例，我在后端驱动模块的代码需要调用这个函数的地方：</p>

<!--more-->




<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='C'><span class='line'><span class="n">target_complete_cmd</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="n">status</span><span class="p">);</span>
</span><span class='line'><span class="n">kfree</span><span class="p">(</span><span class="n">ibr</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>


<p>把它改为这样：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='C'><span class='line'><span class="cm">/* 参考 target_core_mod 里的函数原型，定义函数指针变量 */</span>
</span><span class='line'><span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">target_complete_cmd_ptr</span><span class="p">)(</span><span class="k">struct</span> <span class="n">se_cmd</span> <span class="o">*</span><span class="p">,</span> <span class="n">u8</span><span class="p">);</span>
</span><span class='line'><span class="cm">/* 得到的是 target_complete_cmd 函数入口地址 */</span>
</span><span class='line'><span class="n">target_complete_cmd_ptr</span> <span class="o">=</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">kallsyms_lookup_name</span><span class="p">(</span>
</span><span class='line'>    <span class="s">&quot;target_core_mod_new:target_complete_cmd&quot;</span><span class="p">);</span>
</span><span class='line'><span class="p">(</span><span class="o">*</span><span class="n">target_complete_cmd_ptr</span><span class="p">)(</span><span class="n">cmd</span><span class="p">,</span> <span class="n">status</span><span class="p">);</span>
</span><span class='line'><span class="n">kfree</span><span class="p">(</span><span class="n">ibr</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>


<p>稍微验证了一下，发现可以用了，于是开始改其他的。</p>

<p>结果——WTF! 一共有 40+个被引用的导出符号！一个一个改的话，这后端驱动的代码还能看么？</p>

<p>思来想去，觉得，该改的还是得改。决定写个宏包装一下，尽量提高一下可读性。</p>

<p>以下省略我反复碰壁的过程，直接上成品(some ideas was inspired by <a href="http://constellation.github.io/blog/2014/12/07/importing-hidden-kernel-symbols/">this post</a>, thanks!):</p>

<p>首先，新建一个头文件，把宏和一些函数指针定义都给丢到里面，比如叫<code>import_sym.h</code>:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
</pre></td><td class='code'><pre><code class='C'><span class='line'><span class="cp">#include &lt;linux/kallsyms.h&gt;</span>
</span><span class='line'>
</span><span class='line'><span class="cm">/* 先定义一个结构体存放所有可能用到的函数指针 */</span>
</span><span class='line'><span class="k">struct</span> <span class="n">symbol_set</span> <span class="p">{</span>
</span><span class='line'>    <span class="o">&lt;</span><span class="n">snip</span><span class="p">...</span><span class="o">&gt;</span>
</span><span class='line'>    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">target_complete_cmd</span><span class="p">)(</span><span class="k">struct</span> <span class="n">se_cmd</span> <span class="o">*</span><span class="p">,</span> <span class="n">u8</span><span class="p">);</span>
</span><span class='line'>    <span class="o">&lt;</span><span class="n">snip</span><span class="p">...</span><span class="o">&gt;</span>
</span><span class='line'><span class="p">};</span>
</span><span class='line'>
</span><span class='line'><span class="cm">/* 这个宏用于查找符号地址，并且做一点基本的错误检查 */</span>
</span><span class='line'><span class="cp">#define IMPORT_SYMBOL(name)                                             \</span>
</span><span class='line'><span class="cp">        sym.name = (typeof(name) *)kallsyms_lookup_name(                \</span>
</span><span class='line'><span class="cp">                        &quot;target_core_mod_new:&quot; #name);                  \</span>
</span><span class='line'><span class="cp">        pr_debug(&quot;symbol name: &quot; #name &quot;, symbol addr found: 0x%lx\n&quot;,  \</span>
</span><span class='line'><span class="cp">                (unsigned long)sym.name);                               \</span>
</span><span class='line'><span class="cp">        if (!sym.name)                                                  \</span>
</span><span class='line'><span class="cp">                return -EINVAL;</span>
</span><span class='line'>
</span><span class='line'><span class="cm">/*</span>
</span><span class='line'><span class="cm"> * 下面这两个宏会拼成一个函数，放在前后端驱动中，前面这个宏还定义了一个</span>
</span><span class='line'><span class="cm"> * 全局静态结构用于存放 symbol 的集合</span>
</span><span class='line'><span class="cm"> */</span>
</span><span class='line'><span class="cp">#define IMPORT_SYMBOL_START                                             \</span>
</span><span class='line'><span class="cp">static struct symbol_set sym;                                           \</span>
</span><span class='line'><span class="cp">static int __init_syms_lookup(void)                                     \</span>
</span><span class='line'><span class="cp">{</span>
</span><span class='line'>
</span><span class='line'><span class="cp">#define IMPORT_SYMBOL_END                                               \</span>
</span><span class='line'><span class="cp">        return 0;                                                       \</span>
</span><span class='line'><span class="cp">}</span>
</span><span class='line'>
</span><span class='line'><span class="cm">/* 下面这个宏用来替换前后端驱动中每个要引用原导出符号的语句 */</span>
</span><span class='line'><span class="cp">#define IMPORTED(name) (*sym.name)</span>
</span></code></pre></td></tr></table></div></figure>


<p>接下来改前后端驱动的代码，还是以<code>target_core_iblock</code>为例：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
</pre></td><td class='code'><pre><code class='C'><span class='line'><span class="cp">#include &quot;import_sym.h&quot;</span>
</span><span class='line'>
</span><span class='line'><span class="n">IMPORT_SYMBOL_START</span>
</span><span class='line'><span class="nf">IMPORT_SYMBOL</span><span class="p">(</span><span class="n">target_complete_cmd</span><span class="p">);</span>
</span><span class='line'><span class="cm">/* 这里还可以继续添加 IMPORT_SYMBOL 宏以查找更多需要用的符号 */</span>
</span><span class='line'><span class="n">IMPORT_SYMBOL_END</span>
</span><span class='line'>
</span><span class='line'><span class="o">&lt;</span><span class="n">snip</span><span class="p">...</span><span class="o">&gt;</span>
</span><span class='line'><span class="cm">/* 把 target_complete_cmd(cmd, status); 换掉 */</span>
</span><span class='line'><span class="n">IMPORTED</span><span class="p">(</span><span class="n">target_complete_cmd</span><span class="p">)(</span><span class="n">cmd</span><span class="p">,</span> <span class="n">status</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="o">&lt;</span><span class="n">snip</span><span class="p">...</span><span class="o">&gt;</span>
</span><span class='line'><span class="k">static</span> <span class="kt">int</span> <span class="n">__init</span> <span class="n">iblock_module_init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span><span class='line'><span class="p">{</span>
</span><span class='line'>    <span class="cm">/* 在模块初始化的时候就找齐所有需要的符号地址 */</span>
</span><span class='line'>    <span class="kt">int</span> <span class="n">ret</span> <span class="o">=</span> <span class="n">__init_syms_lookup</span><span class="p">();</span>
</span><span class='line'>    <span class="k">if</span> <span class="p">(</span><span class="n">ret</span><span class="p">)</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">ret</span><span class="p">;</span>
</span><span class='line'>    <span class="k">return</span> <span class="nf">IMPORTED</span><span class="p">(</span><span class="n">transport_subsystem_register</span><span class="p">)(</span><span class="o">&amp;</span><span class="n">iblock_template</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>这样就可以了。</p>

<p>由于不再使用<code>EXPORT_SYMBOL()</code>，做出来的前后端驱动模块会不再显式依赖<code>target_core_mod</code>，必须得确保 core 模块先加载，才能让前后端驱动模块加载之后找到符号地址。卸载的时候 core 模块要最后卸载，否则会碰到空指针引发 Kernel Panic。这算是这个方案的一个不完美之处吧。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Linux Kernel 208.5d Panic Issue]]></title>
    <link href="http://www.casparzhang.com/blog/2015/05/21/208-day-crash-issue/"/>
    <updated>2015-05-21T15:39:33+08:00</updated>
    <id>http://www.casparzhang.com/blog/2015/05/21/208-day-crash-issue</id>
    <content type="html"><![CDATA[<p>最近线上碰到的问题，虽说早就有解决方案了，但是陆陆续续还是碰到很多没有升级内核的机器挂掉。记录一下以下内容，仅供参考。</p>

<p>现象是机器运行到一定天数(刚开始反馈集中在 208.5 天左右)就会主动挂掉，报告文章末尾的 CallTrace，所幸社区几年前就有了解决方案：</p>

<h2>Upstream Patch</h2>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>commit 4cecf6d401a01d054afc1e5f605bcbfe553cb9b9
</span><span class='line'>Author: Salman Qazi &lt;sqazi@google.com&gt;
</span><span class='line'>Date:   Tue Nov 15 14:12:06 2011 -0800
</span><span class='line'>
</span><span class='line'>    sched, x86: Avoid unnecessary overflow in sched_clock
</span><span class='line'>
</span><span class='line'>    (Added the missing signed-off-by line)
</span><span class='line'>
</span><span class='line'>    In hundreds of days, the __cycles_2_ns calculation in sched_clock
</span><span class='line'>    has an overflow.  cyc * per_cpu(cyc2ns, cpu) exceeds 64 bits, causing
</span><span class='line'>    the final value to become zero.  We can solve this without losing
</span><span class='line'>    any precision.
</span><span class='line'>
</span><span class='line'>    We can decompose TSC into quotient and remainder of division by the
</span><span class='line'>    scale factor, and then use this to convert TSC into nanoseconds.
</span><span class='line'>
</span><span class='line'>    Signed-off-by: Salman Qazi &lt;sqazi@google.com&gt;
</span><span class='line'>    Acked-by: John Stultz &lt;johnstul@us.ibm.com&gt;
</span><span class='line'>    Reviewed-by: Paul Turner &lt;pjt@google.com&gt;
</span><span class='line'>    Cc: stable@kernel.org
</span><span class='line'>    Signed-off-by: Peter Zijlstra &lt;a.p.zijlstra@chello.nl&gt;
</span><span class='line'>    Link: http://lkml.kernel.org/r/20111115221121.7262.88871.stgit@dungbeetle.mtv.corp.google.com
</span><span class='line'>    Signed-off-by: Ingo Molnar &lt;mingo@elte.hu&gt;</span></code></pre></td></tr></table></div></figure>


<!--more-->


<h2>Red Hat Fix</h2>

<ul>
<li>Z-stream since 2.6.32-220.5.1.el6</li>
<li>Y-stream Since 2.6.32-226.el6</li>
</ul>


<h2>Ref Links</h2>

<ul>
<li><a href="http://kenichiokuyama.blogspot.hk/2011/12/schedclock-overflow-after-2085-days-in.html">http://kenichiokuyama.blogspot.hk/2011/12/schedclock-overflow-after-2085-days-in.html</a></li>
</ul>


<h2>Call Trace</h2>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
<span class='line-number'>61</span>
<span class='line-number'>62</span>
<span class='line-number'>63</span>
<span class='line-number'>64</span>
<span class='line-number'>65</span>
<span class='line-number'>66</span>
<span class='line-number'>67</span>
<span class='line-number'>68</span>
<span class='line-number'>69</span>
<span class='line-number'>70</span>
<span class='line-number'>71</span>
<span class='line-number'>72</span>
<span class='line-number'>73</span>
<span class='line-number'>74</span>
<span class='line-number'>75</span>
<span class='line-number'>76</span>
<span class='line-number'>77</span>
<span class='line-number'>78</span>
<span class='line-number'>79</span>
<span class='line-number'>80</span>
<span class='line-number'>81</span>
<span class='line-number'>82</span>
<span class='line-number'>83</span>
<span class='line-number'>84</span>
<span class='line-number'>85</span>
<span class='line-number'>86</span>
<span class='line-number'>87</span>
<span class='line-number'>88</span>
<span class='line-number'>89</span>
<span class='line-number'>90</span>
<span class='line-number'>91</span>
<span class='line-number'>92</span>
<span class='line-number'>93</span>
<span class='line-number'>94</span>
<span class='line-number'>95</span>
<span class='line-number'>96</span>
<span class='line-number'>97</span>
<span class='line-number'>98</span>
<span class='line-number'>99</span>
<span class='line-number'>100</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>[776183.791021] divide error: 0000 [#1] SMP
</span><span class='line'>[776183.795055] last sysfs file: /sys/devices/system/cpu/cpu15/cache/index2/shared_cpu_map
</span><span class='line'>[776183.803045] CPU 9
</span><span class='line'>[776183.804967] Modules linked in: &lt;snip&gt;
</span><span class='line'>[776183.844841]
</span><span class='line'>[776183.846415] Modules linked in: &lt;snip&gt;
</span><span class='line'>[776183.886217] Pid: 0, comm: swapper Not tainted &lt;kernel&gt;
</span><span class='line'>[776183.895358] RIP: 0010:[&lt;ffffffff81059e15&gt;] [&lt;ffffffff81059e15&gt;] find_busiest_group+0x485/0xb50
</span><span class='line'>[776183.904258] RSP: 0018:ffff880028283c80 EFLAGS: 00010246
</span><span class='line'>[776183.909703] RAX: 0000000000000000 RBX: 0000000000000000 RCX: ffff88002828f900
</span><span class='line'>[776183.917021] RDX: 0000000000000000 RSI: ffc2c20d09ff2179 RDI: 000000001dcd6500
</span><span class='line'>[776183.924341] RBP: ffff880028283de0 R08: 0002c1ef5f24b5a6 R09: ffff88002828f910
</span><span class='line'>[776183.931658] R10: 0000000000000000 R11: 0000000000000000 R12: ffff880028295f01
</span><span class='line'>[776183.938977] R13: ffff88002828f900 R14: 0000000000015f40 R15: ffffffffffffffff
</span><span class='line'>[776183.946296] FS: 0000000000000000(0000) GS:ffff880028280000(0000) knlGS:0000000000000000
</span><span class='line'>[776183.954568] CS: 0010 DS: 0018 ES: 0018 CR0: 000000008005003b
</span><span class='line'>[776183.960447] CR2: 00000000008cb028 CR3: 0000000c61a84000 CR4: 00000000000006e0
</span><span class='line'>[776183.967776] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
</span><span class='line'>[776183.975095] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
</span><span class='line'>[776183.982418] Process swapper (pid: 0, threadinfo ffff880664a52000, task ffff880c6412f4c0)
</span><span class='line'>[776183.990691] Stack:
</span><span class='line'>[776183.992839] ffff880028283e40 ffff880028283d80 ffff880c7fc29600 ffff880028283e64
</span><span class='line'>[776184.000247] &lt;0&gt; 0000000000000040 ffff880028283e58 0000000960cf4700 ffff88002828f5e0
</span><span class='line'>[776184.008157] &lt;0&gt; 0000000028283ce0 ffff88002828f910 00000001ffffffff 0000000000000009
</span><span class='line'>[776184.016303] Call Trace:
</span><span class='line'>[776184.018884] &lt;IRQ&gt;
</span><span class='line'>[776184.021138] [&lt;ffffffff8105af96&gt;] rebalance_domains+0x146/0x520
</span><span class='line'>[776184.027193] [&lt;ffffffff8105b3bc&gt;] run_rebalance_domains+0x4c/0x150
</span><span class='line'>[776184.033506] [&lt;ffffffff8106b1e0&gt;] __do_softirq+0xc0/0x1e0
</span><span class='line'>[776184.039038] [&lt;ffffffff8100c18c&gt;] call_softirq+0x1c/0x30
</span><span class='line'>[776184.044483] [&lt;ffffffff8100dd55&gt;] do_softirq+0x65/0xa0
</span><span class='line'>[776184.049755] [&lt;ffffffff8106af2c&gt;] irq_exit+0x7c/0x90
</span><span class='line'>[776184.054856] [&lt;ffffffff814b0710&gt;] smp_apic_timer_interrupt+0x70/0x9d
</span><span class='line'>[776184.061343] [&lt;ffffffff8100bb53&gt;] apic_timer_interrupt+0x13/0x20
</span><span class='line'>[776184.067485] &lt;EOI&gt;
</span><span class='line'>[776184.069738] [&lt;ffffffff813bdc8a&gt;] ? poll_idle+0x2a/0x70
</span><span class='line'>[776184.075097] [&lt;ffffffff813bdc73&gt;] ? poll_idle+0x13/0x70
</span><span class='line'>[776184.080454] [&lt;ffffffff813bec86&gt;] ? menu_select+0xa6/0x330
</span><span class='line'>[776184.086072] [&lt;ffffffff813bdbc9&gt;] cpuidle_idle_call+0x99/0x130
</span><span class='line'>[776184.092042] [&lt;ffffffff81009888&gt;] cpu_idle+0xa8/0xe0
</span><span class='line'>[776184.097143] [&lt;ffffffff814a0d64&gt;] ? start_secondary+0x1b4/0x2a0
</span><span class='line'>[776184.103194] [&lt;ffffffff814a0d72&gt;] start_secondary+0x1c2/0x2a0
</span><span class='line'>[776184.109072] Code: d8 b8 01 00 00 00 48 c1 eb 0a 48 85 db 0f 45 c3 41 89 45 08 48 8b 8d 00 ff ff ff 48 8b 45 a8 8b 51 08 48 c1 e0 0a 48 89 d3 31 d2 &lt;48&gt; f7 f3 48 8b 55 b0 48 89 45 a0 31 c0 48 85 d2 74 0f 48 8b 45
</span><span class='line'>[776184.129029] RIP [&lt;ffffffff81059e15&gt;] find_busiest_group+0x485/0xb50
</span><span class='line'>[776184.135529] RSP &lt;ffff880028283c80&gt;
</span><span class='line'>[776184.139477] BUG: scheduling while atomic: swapper/0/0x10000100
</span><span class='line'>[776184.139485] ---[ end trace 32313550323d1731 ]---
</span><span class='line'>[776184.139586] Kernel panic - not syncing: Fatal exception in interrupt
</span><span class='line'>[776184.139589] Pid: 0, comm: swapper Tainted: G D ---------------- &lt;kernel&gt;
</span><span class='line'>[776184.139591] Call Trace:
</span><span class='line'>[776184.139592] &lt;IRQ&gt; [&lt;ffffffff81062e85&gt;] ? panic+0xa5/0x1a0
</span><span class='line'>[776184.139598] [&lt;ffffffff81352006&gt;] ? netoops+0x1c6/0x2a0
</span><span class='line'>[776184.139601] [&lt;ffffffff81064ac5&gt;] ? kmsg_dump+0x135/0x180
</span><span class='line'>[776184.139604] [&lt;ffffffff81062ae5&gt;] ? oops_exit+0x25/0x30
</span><span class='line'>[776184.139606] [&lt;ffffffff814abafb&gt;] ? oops_end+0xeb/0x100
</span><span class='line'>[776184.139609] [&lt;ffffffff8100f07b&gt;] ? die+0x5b/0x90
</span><span class='line'>[776184.139612] [&lt;ffffffff814ab226&gt;] ? do_trap+0x136/0x150
</span><span class='line'>[776184.139616] [&lt;ffffffff8100c80f&gt;] ? do_divide_error+0x8f/0xb0
</span><span class='line'>[776184.139618] [&lt;ffffffff81059e15&gt;] ? find_busiest_group+0x485/0xb50
</span><span class='line'>[776184.139623] [&lt;ffffffff8142124f&gt;] ? ip_rcv+0x26f/0x380
</span><span class='line'>[776184.139626] [&lt;ffffffff8100bdbb&gt;] ? divide_error+0x1b/0x20
</span><span class='line'>[776184.139629] [&lt;ffffffff81059e15&gt;] ? find_busiest_group+0x485/0xb50
</span><span class='line'>[776184.139633] [&lt;ffffffff8105af96&gt;] ? rebalance_domains+0x146/0x520
</span><span class='line'>[776184.139636] [&lt;ffffffff8105b3bc&gt;] ? run_rebalance_domains+0x4c/0x150
</span><span class='line'>[776184.139639] [&lt;ffffffff8106b1e0&gt;] ? __do_softirq+0xc0/0x1e0
</span><span class='line'>[776184.139642] [&lt;ffffffff8100c18c&gt;] ? call_softirq+0x1c/0x30
</span><span class='line'>[776184.139644] [&lt;ffffffff8100dd55&gt;] ? do_softirq+0x65/0xa0
</span><span class='line'>[776184.139647] [&lt;ffffffff8106af2c&gt;] ? irq_exit+0x7c/0x90
</span><span class='line'>[776184.139650] [&lt;ffffffff814b0710&gt;] ? smp_apic_timer_interrupt+0x70/0x9d
</span><span class='line'>[776184.139653] [&lt;ffffffff8100bb53&gt;] ? apic_timer_interrupt+0x13/0x20
</span><span class='line'>[776184.139654] &lt;EOI&gt; [&lt;ffffffff813bdc8a&gt;] ? poll_idle+0x2a/0x70
</span><span class='line'>[776184.139660] [&lt;ffffffff813bdc73&gt;] ? poll_idle+0x13/0x70
</span><span class='line'>[776184.139663] [&lt;ffffffff813bec86&gt;] ? menu_select+0xa6/0x330
</span><span class='line'>[776184.139666] [&lt;ffffffff813bdbc9&gt;] ? cpuidle_idle_call+0x99/0x130
</span><span class='line'>[776184.139669] [&lt;ffffffff81009888&gt;] ? cpu_idle+0xa8/0xe0
</span><span class='line'>[776184.139672] [&lt;ffffffff814a0d64&gt;] ? start_secondary+0x1b4/0x2a0
</span><span class='line'>[776184.139676] [&lt;ffffffff814a0d72&gt;] ? start_secondary+0x1c2/0x2a0
</span><span class='line'>[776184.325813] Modules linked in: &lt;snip&gt;
</span><span class='line'>[776184.367794] CPU 1:
</span><span class='line'>[776184.370036] Modules linked in: &lt;snip&gt;
</span><span class='line'>[776184.412014] Pid: 0, comm: swapper Tainted: G D ---------------- &lt;kernel&gt;
</span><span class='line'>[776184.423621] RIP: 0010:[&lt;ffffffff813bdc8a&gt;] [&lt;ffffffff813bdc8a&gt;] poll_idle+0x2a/0x70
</span><span class='line'>[776184.431644] RSP: 0018:ffff88066495bea8 EFLAGS: 00000246
</span><span class='line'>[776184.437128] RAX: ffff88066495bfd8 RBX: ffff88066495bed8 RCX: 0042e00b649a5000
</span><span class='line'>[776184.444484] RDX: 000000000001f6a9 RSI: ffff88002821d610 RDI: ffffffff81a2fbc0
</span><span class='line'>[776184.451844] RBP: ffffffff8100bb4e R08: 0000000000000000 R09: 0000000000000000
</span><span class='line'>[776184.459201] R10: 0000000000000000 R11: 0000000000000000 R12: 0042dfee0e6ae400
</span><span class='line'>[776184.466567] R13: 0000000000000000 R14: 0000000000000000 R15: 0000000561f5fe16
</span><span class='line'>[776184.473927] FS: 0000000000000000(0000) GS:ffff880028200000(0000) knlGS:0000000000000000
</span><span class='line'>[776184.482241] CS: 0010 DS: 0018 ES: 0018 CR0: 000000008005003b
</span><span class='line'>[776184.488159] CR2: 00000000427b7078 CR3: 0000000651611000 CR4: 00000000000006e0
</span><span class='line'>[776184.495520] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
</span><span class='line'>[776184.502878] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
</span><span class='line'>[776184.510236] Call Trace:
</span><span class='line'>[776184.512861] [&lt;ffffffff813bdc73&gt;] ? poll_idle+0x13/0x70
</span><span class='line'>[776184.518261] [&lt;ffffffff813bec86&gt;] ? menu_select+0xa6/0x330
</span><span class='line'>[776184.523918] [&lt;ffffffff813bdbc9&gt;] ? cpuidle_idle_call+0x99/0x130
</span><span class='line'>[776184.530096] [&lt;ffffffff81009888&gt;] ? cpu_idle+0xa8/0xe0
</span><span class='line'>[776184.535408] [&lt;ffffffff814a0d64&gt;] ? start_secondary+0x1b4/0x2a0
</span><span class='line'>[776184.541499] [&lt;ffffffff814a0d72&gt;] ? start_secondary+0x1c2/0x2a0</span></code></pre></td></tr></table></div></figure>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Blog 恢复]]></title>
    <link href="http://www.casparzhang.com/blog/2015/05/21/blog-revived/"/>
    <updated>2015-05-21T15:17:37+08:00</updated>
    <id>http://www.casparzhang.com/blog/2015/05/21/blog-revived</id>
    <content type="html"><![CDATA[<p>Blog 已经迁移到了 <a href="http://www.github.com">GitHub</a>，准备恢复更新，主要更新一些内核相关的内容(瞬间变得高大上了有没有~lol)。</p>

<p>且看看我这会儿能坚持多久吧。</p>

<p>对了，坚持用 FeedBurner 订阅的用户，跳出来冒个泡呗…… 估计已经没有多少了吧？</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Fix Razer Orochi Re-connect Issue]]></title>
    <link href="http://www.casparzhang.com/blog/2014/10/10/fix-razer-orochi-re-connect-issue/"/>
    <updated>2014-10-10T00:00:00+08:00</updated>
    <id>http://www.casparzhang.com/blog/2014/10/10/fix-razer-orochi-re-connect-issue</id>
    <content type="html"><![CDATA[<h2>Issue</h2>

<p>雷蛇八歧大蛇 2013 版鼠标，短时间内不用自动休眠，系统中蓝牙连接断(预期行为)，鼠标从休眠恢复后系统中的蓝牙连接却无法自动重连，只能将鼠标置为配对模式，通过系统中的蓝牙工具手工连接。</p>

<h2>Environment</h2>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>Razer Orochi 蓝牙鼠标
</span><span class='line'>Gentoo + KDE
</span><span class='line'>kernel 3.10 ~ 3.16
</span><span class='line'>bluez 4.101 ~ 5.23
</span><span class='line'>bluedevil 1.3.2 ~ 2.0_rc1</span></code></pre></td></tr></table></div></figure>


<h2>Solution</h2>

<!--more-->


<p>通过 <code>hcidump</code> 工具发现：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>&gt; HCI Event: Connect Request (0x04) plen 10
</span><span class='line'>    bdaddr F0:65:DD:94:9F:BB class 0x002580 type ACL
</span><span class='line'>&gt; HCI Event: Command Status (0x0f) plen 4
</span><span class='line'>    Accept Connection Request (0x01|0x0009) status 0x00 ncmd 1
</span><span class='line'>&gt; HCI Event: Connect Complete (0x03) plen 11
</span><span class='line'>    status 0x00 handle 13 bdaddr F0:65:DD:94:9F:BB type ACL encrypt 0x00
</span><span class='line'>&gt; HCI Event: Command Status (0x0f) plen 4
</span><span class='line'>    Read Remote Supported Features (0x01|0x001b) status 0x00 ncmd 1
</span><span class='line'>&gt; HCI Event: Read Remote Supported Features (0x0b) plen 11
</span><span class='line'>    status 0x00 handle 13
</span><span class='line'>    Features: 0xbf 0x00 0xa0 0x78 0x18 0x1e 0x59 0x83
</span><span class='line'>&gt; HCI Event: Command Status (0x0f) plen 4
</span><span class='line'>    Read Remote Extended Features (0x01|0x001c) status 0x00 ncmd 1
</span><span class='line'>&gt; HCI Event: Read Remote Extended Features (0x23) plen 13
</span><span class='line'>    status 0x00 handle 13 page 1 max 1
</span><span class='line'>    Features: 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00
</span><span class='line'>&gt; HCI Event: Command Status (0x0f) plen 4
</span><span class='line'>    Remote Name Request (0x01|0x0019) status 0x00 ncmd 1
</span><span class='line'>&gt; HCI Event: Remote Name Req Complete (0x07) plen 255
</span><span class='line'>    status 0x00 bdaddr F0:65:DD:94:9F:BB name 'Razer Orochi 2013'
</span><span class='line'>&gt; HCI Event: Command Complete (0x0e) plen 10
</span><span class='line'>    Link Key Request Negative Reply (0x01|0x000c) ncmd 1
</span><span class='line'>    status 0x00 bdaddr F0:65:DD:94:9F:BB
</span><span class='line'>&gt; HCI Event: Disconn Complete (0x05) plen 4
</span><span class='line'>    status 0x00 handle 13 reason 0x13
</span><span class='line'>    Reason: Remote User Terminated Connection</span></code></pre></td></tr></table></div></figure>


<p>出现 <strong><em>Link Key Request Negative Reply</em></strong> 的字样，怀疑是配对的时候使用自动 key，而蓝牙工具没有保存这个自动 key，导致鼠标从休眠恢复时试图以空 key 连接，从而失败。</p>

<p>知道了问题，解决方法就简单了，配对的时候手工输入 key/PIN 为 <strong><em>1234</em></strong>，连接即可。</p>

<p>就这么一个破问题，折腾了我半天时间，内核、bluez、bluedevil 都尝试了一遍，还在人家的 Macbook 上测试了一番。差点就冲动下单换 Mac 了。 <code>#我不是土豪#</code></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Gentoo KDE 下折腾 OpenConnect]]></title>
    <link href="http://www.casparzhang.com/blog/2014/03/21/configure-openconnect-in-gentoo-kde/"/>
    <updated>2014-03-21T00:00:00+08:00</updated>
    <id>http://www.casparzhang.com/blog/2014/03/21/configure-openconnect-in-gentoo-kde</id>
    <content type="html"><![CDATA[<p>最近呢，推上某奸商推出了 AnyConnect 套餐，这对广大翻墙群众来说绝对是个利好消息啊，可以自动配路由的东东。我毫不犹豫去把自己的绝版廉价套餐换成了 100 元的套餐，然后开始折腾 AnyConnect 配置。</p>

<p>iOS 上配置十分简单，找到 Cisco AnyConnect 这个 App 就搞定。Linux 下要把它配置得很舒服，着实花了一番功夫。</p>

<p>公司 VPN 用的也是 AnyConnect，我就继续用着公司的客户端(Cisco AnyConnect Secure Mobility Client), 直到今天早上我修复了笔记本上的无线网，机器有了两个 IP 为止。有了两个 IP 的 AnyConnect service 居然 segfault 了，看了一下 debug 信息，我觉得对这种闭源工具我还是别折腾了。直接换开源方案 OpenConnect.</p>

<p>Portage 里搜了一下，openconnect 有两个包，networkmanager-openconnect 有 libkeyring-gnome 依赖，我现在是个有洁癖的 KDE 党，果断不能装啊，于是用第二个， openconnect 命令行版…… 不行，不能这么罗里八嗦，反正最后折腾结果如下：</p>

<!--more-->


<ol>
<li>只用命令行的话，Portage 里的 <strong><em>openconnect-5.99*</em></strong> 版本可用，配好 <code>/etc/conf.d/openconnect</code> 就可以了；</li>
<li>需要配合 KDE plasma 插件 <strong><em>networkmanagement</em></strong> (0.9+版本) 的话，就必须得装 <strong>NetworkManager-openconnect</strong> 了。事实证明，<strong>libkeyring-gnome</strong> 依赖是可以去掉的。我修改了 Portage 文件，放到我<a href="https://github.com/casparant/caspar-gentoo/tree/master/net-misc/networkmanager-openconnect">自己的 Overlay</a> 里了。</li>
<li><del datetime="2014-03-21T09:54:44+00:00">另外 <strong><em>openconnect-5.99*</em></strong> 版本会导致 <strong><em>networkmanagement</em></strong> 带了 <code>openconnect</code> flag 之后编译失败，只能回退到 5.03 版本。</del> <strong><em>networkmanagement</em></strong> 已经 deprecated 了，用 <strong><em>plasma-nm</em></strong> 代替，最新版本没有编译问题。</li>
<li><del datetime="2014-03-21T09:54:44+00:00"><strong><em>openconnect-5.03</em></strong> 版本也是有问题的，<code>openconnect</code> 命令执行后，如果从终端输入密码，需要先按一个回车，再输入一遍。所以配置文件中的 <code>tmp_SERVER &lt;&lt;-E</code> 后面需要加一个空行，否则 daemon 永远启动失败，因为接收不到密码。</del> 换了最新的 <strong><em>openconnect-5.99</em></strong> 之后，没问题了。</li>
</ol>


<p>就这些。配好之后，既可以用 NM 的 GUI 连，也可以起服务连。从此过上了永不断网（某奸商广告语）的生活～</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[WizNote: 终于找到比较干净的 Qt 的笔记软件]]></title>
    <link href="http://www.casparzhang.com/blog/2014/03/11/an-introduction-of-wiz-note/"/>
    <updated>2014-03-11T00:00:00+08:00</updated>
    <id>http://www.casparzhang.com/blog/2014/03/11/an-introduction-of-wiz-note</id>
    <content type="html"><![CDATA[<p>Linux 上记笔记，向来很纠结。以前用过一段时间 <a href="http://zim-wiki.org/index.html">Zim</a>，后来变成了 KDE 党，只好果断抛弃了。找到 <a href="http://basket.kde.org/">BasKet</a>，功能还是很强大的，可是格式一团糟。无法以 Plain Text 存储文字，拷贝来拷贝去的时候经常格式混乱。也尝试在 Web 端存东西，结果还是发现不习惯，而且离线无法访问。</p>

<p>然后今天工作的时候有同事提到客户报告鄙厂<a href="http://www.aliyun.com/">产品</a>的问题，碰到了故障（当然在内核组大牛们的努力下该问题已经解决了），我就好奇去看了一下报告者信息，然后就发现了这个笔记软件：<a href="http://wiz.cn/index.html">Wiz</a>. 看起来还挺 Geek 的，有手机端有 Linux 端有 Mac 端。于是去 gentoo-zh 的 Overlay 找了一圈（是的，5 年过去了我还在 Gentoo 的不归路上慢慢走着），<a href="https://github.com/microcai/gentoo-zh/blob/master/app-text/wiznote/wiznote-2.0.64.ebuild">找到了</a>。</p>

<p>看起来样子还不错，还比较清爽，也可以格式化为 Plain Text。功能上比 BasKet 少一些，但是用着还是挺舒服的。</p>

<p>最不能忍受的问题是没有任务栏图标，于是接着搜。</p>

<p>找到了一年前某人的一个没有 Merge 的 <a href="https://github.com/WizTeam/WizQTClient/pull/80">Pull Request</a>，然后稍微修改了一下补丁，打到源代码里，发现可以用了。</p>

<p>修改后的 ebuild 请<a href="https://github.com/casparant/caspar-gentoo/blob/master/app-text/wiznote/wiznote-2.1.0_beta.ebuild">猛戳</a>。</p>

<p>最后，有更靠谱的笔记软件请推荐。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[MCE 的一些零散记录]]></title>
    <link href="http://www.casparzhang.com/blog/2013/06/06/some-misc-items-of-mce/"/>
    <updated>2013-06-06T00:00:00+08:00</updated>
    <id>http://www.casparzhang.com/blog/2013/06/06/some-misc-items-of-mce</id>
    <content type="html"><![CDATA[<p>以前也测过 MCE 相关的东西，但是惭愧，一直不清不楚。这两天稍微整理了一下相关知识，感觉见识还是很浅，记些零碎的东西在这，权当博客复活第一篇。如有理解错误，欢迎指出。</p>

<p>CPU 检测到硬件错误时，内核会报 Machine-check，根据硬件错误是否可以修正(CE, Correctable Error; UCE/UE, Un-correctable Error)，内核做出不同反应。CE 的话内核只把相关信息写到一个字符设备 <code>/dev/mcelog</code> 中，UE 的话是会在记录相关信息之余，还会做出不同处理，比如中断当前遇错的应用程序，或者 Panic. <code>[TODO: 这块相关的内核代码还得再看几遍]</code></p>

<p><code>/dev/mcelog</code> 中的信息可以用用户空间工具 <strong><a href="https://github.com/andikleen/mcelog">mcelog</a></strong> 来读取。<strong>mcelog</strong> 比较有意思的一个参数是 <code>--dmi</code>，可以尝试解析 ADDR 字段以获取诸如内存出厂信息，DIMM 位置等有用的信息，可是很遗憾在实际环境中这些 DIMM 信息基本上都是错的。 (mcelog 的 man page 说了，不要怪 Linux，得怪那稀奇古怪的主板厂商……)</p>

<p>man page 还有一个地方提到：</p>

<blockquote><p>The kernel prefers old messages over new. If the log buffer overflows only old ones will be kept.</p></blockquote>

<!--more-->


<p>查看内核代码中 <code>arch/x86/include/asm/mce.h</code>，有：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='C'><span class='line'><span class="cp">#define MCE_LOG_LEN 32</span>
</span><span class='line'>
</span><span class='line'><span class="k">struct</span> <span class="n">mce_log</span> <span class="p">{</span>
</span><span class='line'>        <span class="kt">char</span> <span class="n">signature</span><span class="p">[</span><span class="mi">12</span><span class="p">];</span> <span class="cm">/* &quot;MACHINECHECK&quot; */</span>
</span><span class='line'>        <span class="kt">unsigned</span> <span class="n">len</span><span class="p">;</span>       <span class="cm">/* = MCE_LOG_LEN */</span>
</span><span class='line'>        <span class="kt">unsigned</span> <span class="n">next</span><span class="p">;</span>
</span><span class='line'>        <span class="kt">unsigned</span> <span class="n">flags</span><span class="p">;</span>
</span><span class='line'>        <span class="kt">unsigned</span> <span class="n">recordlen</span><span class="p">;</span>     <span class="cm">/* length of struct mce */</span>
</span><span class='line'>        <span class="k">struct</span> <span class="n">mce</span> <span class="n">entry</span><span class="p">[</span><span class="n">MCE_LOG_LEN</span><span class="p">];</span>
</span><span class='line'><span class="p">};</span>
</span></code></pre></td></tr></table></div></figure>


<p>这里的 <code>MCE_LOG_LEN</code> 就是 man page 中提到的 log buffer 长度。</p>

<p>内核中还有个 <code>mce_inject</code> 模块（<code>CONFIG_X86_MCE_INJECT=m/y</code>）,可以用于产生一些假的 MCE 以测试相关功能。需要配合用户空间工具 <strong><a href="https://github.com/andikleen/mce-inject/">mce-inject</a></strong> 来使用。</p>

<p>如果要做一个比较完整的测试，<strong><a href="https://github.com/andikleen/mce-test">mce-test</a></strong> 工具可以帮忙，其中会涉及一些 RAS 特性，比如 <strong><em>hwpoison</em></strong>(<code>Documentation/vm/hwpoison.txt</code>)，不过我现在的活儿不怎么管测试，暂时就没去回顾这个测试工具了。</p>

<p>先记这些，回头把细节都搞懂了再补充……</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Resolved: 如何更新 Git 补丁]]></title>
    <link href="http://www.casparzhang.com/blog/2012/01/03/how-to-update-your-git-patches/"/>
    <updated>2012-01-03T00:00:00+08:00</updated>
    <id>http://www.casparzhang.com/blog/2012/01/03/how-to-update-your-git-patches</id>
    <content type="html"><![CDATA[<p>使用 git 制作补丁时，经常发现补丁需要修改。如果只是最后一次 commit 需要修改，那就好办，用下面的方法就可以搞定：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>git reset HEAD^
</span><span class='line'><span class="c"># edit edit edit</span>
</span><span class='line'>git commit -a -s -c ORIG_HEAD
</span><span class='line'>git format-patch --subject-prefix<span class="o">=</span><span class="s2">&quot;PATCH v2&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<p>但是如果是一系列补丁中的中间几个补丁需要修改，该怎么办呢？</p>

<p><del datetime="2012-01-03T16:12:57+00:00">笨办法已经被删掉>.&lt;</del></p>

<!--more-->


<p><strong>Update 1:</strong> 非常感谢 wangcong 的指点！<code>git rebase -i</code> 可以出色地完成这个任务，方法在 man-page 里面有详述，见 <strong><em>INTERACTIVE MODE</em></strong> 和 <strong><em>SPLITTING COMMITS</em></strong> 部分。</p>

<p>假设当前所在分支名为 <strong>topic</strong> ，做了 3 个补丁，打算提交给邮件列表的时候发现中间一个补丁需要更新，此时使用下列命令：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>git rebase -i HEAD~4
</span></code></pre></td></tr></table></div></figure>


<p>出现编辑窗口如下：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>pick 21732a8 mm/swapping01: new testcase
</span><span class='line'>pick c3751d1 hugemmap01: clean-up format
</span><span class='line'>pick d75449c mm/swapping01: change sleep <span class="nb">time </span>to <span class="m">5</span> sec
</span></code></pre></td></tr></table></div></figure>


<p>此时需要修改 <code>commit c3751d1</code>，则把 <code>pick</code> 改为 <code>edit</code> 即可。保存退出后 git 会 rebase 到需要修改的 commit 处停止，此时可以直接修改内容，然后执行：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>git add xxx <span class="c"># mark as resolved</span>
</span><span class='line'>git commit --amend
</span></code></pre></td></tr></table></div></figure>


<p>或者</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>git reset --soft HEAD^
</span><span class='line'><span class="c">#edit edit edit</span>
</span><span class='line'>git commit -a -s -c ORIG_HEAD
</span></code></pre></td></tr></table></div></figure>


<p>之后执行 <code>git rebase --continue</code> 即可。</p>

<p>突然想起来一年前就请教过 wangcong 这个问题，只不过当时对 <code>git rebase</code> 完全不了解，后来就忘了，惭愧 -_-|||。</p>

<p><strong>Update 2</strong>: 同样感谢 Iven 的指点，可以去看看他的文章: <a href="http://www.kissuki.com/?p=135">http://www.kissuki.com/?p=135</a></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[CM7.1 on DEFY]]></title>
    <link href="http://www.casparzhang.com/blog/2011/10/12/cm7-1-on-defy/"/>
    <updated>2011-10-12T00:00:00+08:00</updated>
    <id>http://www.casparzhang.com/blog/2011/10/12/cm7-1-on-defy</id>
    <content type="html"><![CDATA[<p>CyanogenMod, the community Android mod, is officially <a href="http://www.cyanogenmod.com/blog/cyanogenmod-7-1-released" target="_blank">supporting Motorlora Defy</a> in the latest release CM7.1. Now the system seems running well on my device.</p>

<p>Why I want CM7.1 on my machine:</p>

<p><ul>
	<li>try something new</li>
	<li>I need openvpn</li>
	<li>2.3.4 ROM for ME525+ not working well on my device :-(</li>
</ul></p>

<p>I followed most of the instructions from <a href="http://forum.cyanogenmod.com/topic/32469-cyanogenmod-7-for-the-motorola-defy-v710-9-oct-2011/" target="_blank">the post in CM forum</a>, except that I didn't install gapps-gb-20110828. For some reason, <em>Android Market</em> from 20110828 package continously got crashed once I opened the application. I tried with <a href="http://goo-inside.me/gapps/gapps-gb-20110613-signed.zip" target="_blank">gapps-gb-20110613</a> and <em>Android Market</em> worked as normal, so I recommend using this version of gapps if you ever encountered similar issue like me.</p>

<p><!--more-->Well... Some extra jibber-jabbers about <em>Android Market</em>:</p>

<p>Region restriction sucks... It prevents me from buying Touchdown license from market...</p>

<p>I do hate <a href="http://twidroyd.com/" target="_blank">some</a> apps changing their names so frequently and deprecating the old version in market. I PAID for the old one, and I still need to pay for the new one!</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[在 Red Hat 两年]]></title>
    <link href="http://www.casparzhang.com/blog/2011/07/16/beginning-of-my-third-year-in-red-hat/"/>
    <updated>2011-07-16T00:00:00+08:00</updated>
    <id>http://www.casparzhang.com/blog/2011/07/16/beginning-of-my-third-year-in-red-hat</id>
    <content type="html"><![CDATA[<p>已经进入我在 Red Hat 的第三年了，看到两年来这贫瘠无比的博客，决定把阶段总结发到这里来，挽回一下我那正在疯狂下滑的订阅数，嗯。回头看了一下以前的日志，去年这个时候竟然没有写总结，干脆两年的一起写好了。行文照旧会比较混乱，请将就将就。</p>

<p>刚以实习生身份进入 Red Hat 时，主要工作就是验 Bug。记得当时很多 Bug 的描述都很不清晰，往往一个 Bug 就浪费一两天时间去分析。有的 Bug 还不能用脚本重现，只能物理接触硬件。有一次在机房里呆了一个下午，要用针头去戳机器上的一个小孔[<a href="http://en.wikipedia.org/wiki/Non-maskable_interrupt" target="_blank">1</a>]，戳了我整整一个下午。类似的折腾人的 Bug 仿佛是永远都不会消失，现在我还在跟他们打交道。</p>

<p>一个月后，老板让我了解一个测试套件<a href="http://ltp.sourceforge.net/" target="_blank">LTP</a>，之前是他一直在维护这个工具在内部测试平台上的正常运行，交给我之后他基本上就没再怎么管过它了（直到最近，他开始往 LTP 提交内存相关的测试代码，这是后话了）。我当时的基本工作就是定期把 LTP 的稳定发布版本移植回内部测试平台，然后如果运行出错，根据运行情况如果是 LTP 的问题，就写一些补丁来修复 LTP 运行时出现的错误。从此我才正式结束和开源项目社区浅尝辄止的试水活动，开始深入社区贡献代码。为 LTP 编写补丁对我的帮助很大，一方面跟编写补丁相关的工具，比如说 git，都熟练掌握了；另一方面通过熟悉 LTP 的测试代码，了解了很多从用户空间测试内核功能的方法；而且我也学会了怎么在邮件列表里面跟开发者吵架:)</p>

<p><!--more-->随后的几个月里，基本上就是继续了解一些公司内部的测试工具，同时照旧维护 LTP，验 Bug，写自动化测试脚本，分析测试结果。期间出错也不少，比如 Bug 回错地方让人误解这算是小事了，把巨大的二进制包 checkin 到 CVS 仓库里面（从美国那边同步一次得十几分钟）也算是小事了。有一次是让我分析自动化测试结果，结果马马虎虎没仔细看，就认为内核通过测试了，结果内核发布给客户之后被发现了问题，回过头来一看就是我当时马虎漏掉的那段，于是被老板叫过去促膝长谈了。这也是我当时跟客户关系最接近的一次，囧- -|||。还有一次是测试一个网络有关的 Bug，结果我在远程机器上做的测试，洪水般的数据包把整个公司在所有办公室的测试环境都搞挂了……所幸的是类似的问题后来从来没犯过。</p>

<p>而真正理解软件测试的过程是在半年后了，那时候老板让我尝试编写一个测试项目的测试计划，当然后来因为过年回家，没做成，美国的同事接过去做了。不过慢慢开始知道测试计划、测试用例和测试脚本之间的关系了。我们整个组看起来也是一个刚形成规范的组，现在组里在用的一些测试计划都是从那时候开始遵循<a href="http://wilma.vub.ac.be/~se1_0607/svn/bin/cgi/viewvc.cgi/documents/standards/IEEE/IEEE-STD-829-1998.pdf" target="_blank">IEEE829</a>写的。同时组里开始招人，依据内核子系统功能划分测试任务。恰好同时隔壁组的新版测试用例管理系统(上游版本<a href="https://fedoraproject.org/wiki/Nitrate" target="_blank">在此</a>)上线了，每个测试项目的成员把一些具体的测试用例都写到那上面，确实看起来更规范也更清晰了一些。</p>

<p>2010 年上半年准备毕业设计，老板又给了我另外一个开源社区的测试套件<a href="http://crackerjack.sourceforge.net/" target="_blank">Crackerjack</a>来移植，作为我毕设的内容。最后移植是成功了，只是那个开源社区几乎都没人参与了，后来我向管理员要了个 commit 权限，不过因为项目活跃度太低，而且里面很多代码都已经移植到了 LTP 中，也就抛下了。</p>

<p>10 年中旬结束学生身份，正式入职，我的工作重心继续在内核测试工具这一块，有之前一年的积累，我敢说我是全组对测试工具第二了解的人了。同时领到了新的任务，带实习生(美其名曰：Intern Tech Lead)，简而言之，就是实习生在测试时碰到问题就来找我，其中有很多问题肯定是跟测试工具相关的（吐槽：我还真是适合干这个活啊>.<）。既然继续做测试工具，我又领到一些测试工具相关的活，这些工具大多是 Python 写的，于是我又被迫无师自通地学会了 Python。在此期间我深深感受到一件事情，碰到问题，看代码是最有效的 debug 方式。当然我不推荐碰到内核问题直接去看内核代码，那对不了解的人来说是一个黑洞，一陷进去就会浪费掉好久的时间。</p>

<p>10 年下半年有个重要事件，RHEL6 发布了，刚好在我生日前后。公司搞了个庆祝活动，其实也就把自助餐厅搬到了公司里面而已，可能后续还有抽奖什么的，我反正没参加=.= 老大让每个组的负责人都上去讲讲心得，我老板上去之后把组里每个人感谢了一遍，说到我的时候我才发现原来我是个打杂的……因为在 RHEL6 发布前，我把组里每个人的活，除了存储测试之外，都干了一遍。<a href="http://wangcong.org/blog/?p=1416" target="_blank">kdump</a>测试人手不足的时候，我上去顶了一个星期的班；<a href="http://dup.et.redhat.com/" target="_blank">DUP</a>需要交接工作的时候，我就作为过渡人员测试一段时间；<a href="https://fedorahosted.org/v7/" target="_blank">v7</a>没人测的时候，我就承担了测试任务……当时就觉得挺忙的，不过多打打杂，开阔开阔眼界，也是挺开心的事情。得感谢老板让我多方面了解内核测试的内容。</p>

<p>11 年初，老板开始突然向 LTP 提交了很多内存测试的代码，代码被上游接受后，他把内存管理的测试连同测试计划，测试用例都丢给了我，于是我的任务又增加了一个。不过终于有机会开始直接接触内核核心组件了，心里很兴奋。可惜老板留下的代码带着一身 BUG，我花了平均每个月 20 个补丁的代价才把它们修得差不多，而且现在还在继续修。</p>

<p>从去年校园招聘前后开始，我就参与了一系列面试。现在组里有几个人就是我当时面试定下来的。自己主要面试的是应聘实习或者校园招聘的学生，因为自己也刚从学生过来，所以比较能理解面试者的心态。学生面试的时候一般会关心测试职位有没有技术含量，自己能不能学到东西。我总是喜欢拿自己在 Red Hat 之前一年的经历，特别是自己打杂的经历跟他们讲。我面试时也带着一些倾向性，那些偏 Geek 的，给开源社区做过贡献的，阅读过内核代码的，我就比较喜欢。不过根本一些的层次，我还是喜欢学习能力、理解能力、沟通能力强的。</p>

<p>有了之前一年半的测试经验积累，我对测试本身的流程也比较了解了，于是现在开始参加一些更偏向于组内的测试过程控制的事情。这才理解到，所谓 QA，不仅仅是写几个测试计划，验几个 bug 就完事的。测试周期开始前，要保证测试计划的完整；测试开始后，要定期保证测试进度；测试中发现新的 Bug，要根据严重程度及时跟进 DEV/PM 那边的状态……刚开始干这活，完全没有经验，所以一个测试周期快结束的时候，笔记本里就满是总结经验教训。而在项目进度接近尾声的时候，往往是加班的疯狂时期。我当时既承担内存测试任务，跟踪组内的测试进度，还负责了 v7 那个项目的测试管理。虽说后两项不用我自己一件一件去做，但是碰到人手交接什么的活时，经验不足的我为了保证进度，只有自己上阵了，因此加班是不可避免的。在 v7 发布进入尾声的时候，碰到了几个严重的 bug，于是我半夜跟美国那边的开发者同步跟踪进度。他一出修复版本，我就马上测试，甚至他来不及修复的时候，我就自己动手修了>.< 最后总算是把产品发布出去了，也因此搞得身心俱疲。</p>

<p>然后就是最近的一两个月，一边带实习生，一边准备新项目的计划阶段。最近组里的同志们似乎又被测试工具相关的问题缠住了，于是相应的，我又有点事情要忙了，~o(>_<)o~</p>

<p>付出的代价大，积累的经验教训多，收获就多，这东西跟玩仙剑是类似的。就算不给我涨工资，我也觉得这两年学到的东西很值得。当然是否值得是要看有没有建立在时常总结分析经验教训的基础上的。除此之外我觉得还有一些值得分享的东西：</p>

<p>0. 开有效率的会很有用。开会是相互沟通的重要方式，组会之前做好充分准备，尽量减少无用的讨论时间，可以提高效率。开一对一的会议对了解组员的进度和想法有很大帮助。碰到问题用邮件解释不清的时候，最好的解决方案之一就是和同事约个时间开个会。</p>

<p>1. 发散学习会让自己学到的东西更多。做项目不仅仅是做项目，通过项目来了解一些属于自己职责之外的东西，比如趁机会了解一些内核代码，这样会让自己进步更快。但是不能影响自己项目的进度。</p>

<p>2. As usual, to Be Filled... :D</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[关于简历的杂碎]]></title>
    <link href="http://www.casparzhang.com/blog/2011/03/03/jibber-jabber-on-the-resumes/"/>
    <updated>2011-03-03T00:00:00+08:00</updated>
    <id>http://www.casparzhang.com/blog/2011/03/03/jibber-jabber-on-the-resumes</id>
    <content type="html"><![CDATA[<p>最近在帮组里招人，不过一直没有招到合适的（去年校园招聘时候招到过一个合适的，结果被隔壁<a href="http://twitter.com/#!/guaneryu" _target="new">@GMoto</a>组里给抢去了……泪了个奔的）。一直不停地在系统里刷简历，邮件列表里收简历，于是有些碎碎念就冒出来了。想到我不久以前也是简历那一头的角色，现在变成站在这一边的角色了，正好可以忆苦思甜，对比一下。丢在这里，大家凑合着看。</p>

<p>首先得先介绍一下我所在的公司的背景，因为不同公司对待简历有不同的风格。好多人都知道，我现在呆在 Red Hat，就是那个跟某快递公司重名的做 Linux 的公司。至少我们研发部门这边，不会因为简历没写称呼没写落款就不看，也不会因为简历排版混乱就丢在一旁，甚至发现提交的是个 Word 文档，或者联系邮箱留了个带 qq 号码的 qq 邮箱也会认真的去看——只不过第一印象和心理上已经有些想法而已。一般来说，我们喜欢排版简洁的正文简历和 pdf 简历，如果能提交个 odt 格式的也能感觉出投简历的人确实是用心的。另外不是为了装 X，我们要求中英文简历是因为，如果面试结果良好，通常把简历会转交给国外的同事看，决定是否进一步面试。</p>

<p>小结一下，不管简历样式有多糟糕，只要能打开，我们就会认真看。当然非常不幸地，这也是我觉得某些提交简历的同学是折翼的天使的原因。</p>

<p><!--more-->然后是简历的内容。有些简历——真是太折翼了！一看就知道是海投用的“一简历走天下”的那种。某种程度上智联招聘起了为虎作伥的作用，可恨的是我们还不得不用智联……这些简历，“求职目标”一栏把能想到的 IT 职位估计都填遍了，互联网，软件，硬件，开发，测试，通信，嵌入式神马的都写着；然后在项目经历里面大书特书 Oracle 数据库、JavaSE/JavaEE 框架、C#，更发指的是写着有很丰富的 Windows 下某过气的图形界面库编程经验；而掌握技能里面，要么就是只写会 Java，要么就是把听说过的所有语言，从 HTML 到.Net 都写上……我能体会作为一个学生投简历时广撒网捕大鱼的心理和写简历时生怕简历单薄的心情，但是最起码投简历之前也要对应聘职位做一些分析吧，对方是"Linux""内核""测试"职位，三个关键字，至少在简历里要提到一项相关的吧……</p>

<p>针对简历内容，我个人有一些建议，觉得不对的就直接忽略吧。首先是求职目标，对方招什么职位，求职目标一栏就改成什么职位，或者这个职位的大类，别太大，更别写得明显不对口。其次作为学生，项目经历可能不多，那么就在教育经历和掌握技能上稍微补充一些：教育经历一栏可以把跟应聘职位相关的课程列出来，或者把所有重要课程列出来，职位相关的课程加粗醒目表示。这样面试的时候一般都会根据上过的课程问一下相关的理论知识；另外掌握技能这一栏，就要对职位描述作更细致的审察，对方要求什么技能，就写什么技能，哪怕是职位描述上写着“对 XXX 技术有热情，有自主学习能力”，也可以在技能一栏写上对应的话（自学能力不也是一种技能么）。而项目经历一栏，就别填发过传单搭过 Windows 服务器的事情了，宁愿你写一些学校学院布置过的大作业和实训。如果有 Linux 实验和大作业，完全是可以写到项目经历里面的，只要你曾做过。另外每个项目最好能多写几句，一是强调自己个人在项目中的职责，二是技术要点，三可以是项目的难点和解决方案，四可以是自己负责的部分的结果。当然写每点都要有概括成一行文字的能力，长篇大论也是不讨喜的。再小结一下，简历内容一定要有针对性，让人看得出你是用心投这个职位的；技能、知识、经历都与职位要求相关最好，无关的不写或尽量一笔带过。</p>

<p>接下来是一些细节的问题，想到什么说什么。比如说错别字要避免，专业词汇不要拼错；中英文简历排版最好统一，内容保持对应；描述不要夸张，特别是对于技能掌握程度的描述，避免用“精通”一词，因为还有“熟悉”“掌握”“了解”等表示程度的词语；态度要诚恳，用心写的话看的人能看出其中的态度是否诚恳。<em>其他的暂时想不起来了，待补充。</em></p>

<p>最后是针对我个人，或者说我和我周围这群体的人觉得能加分的细节：简历中带一个技术博客或者开源项目的链接；简历后附带一段自己写的 C/Shell/Python/Perl 代码；用 LaTeX 写简历；有 RHCE 的证书。<em>其他的又想不起来了，待补充。</em></p>

<p>不知道订阅我博客的五六百人都是什么群体，居然能忍受我半年不更新博客……要是是学生居多，希望上面的闲言碎语有一些能对你有帮助。当然希望能给我投简历，如果你是一个 Linux 和开源软件的爱好者。过一段时间如果我面试面得郁闷了，可能会发一个关于面试的碎碎念。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Libfprint Finally Supports Thinkpad T410]]></title>
    <link href="http://www.casparzhang.com/blog/2010/08/20/libfprint-finally-supports-thinkpad-t410/"/>
    <updated>2010-08-20T00:00:00+08:00</updated>
    <id>http://www.casparzhang.com/blog/2010/08/20/libfprint-finally-supports-thinkpad-t410</id>
    <content type="html"><![CDATA[<p>I have been keeping tracking of <a href="https://bugzilla.redhat.com/show_bug.cgi?id=504399" target="_blank">this RHBZ</a> for long, and I am happy to see libfprint finally supports my fingerprint reader on T410. Seems that the original author Daniel(dsd) was very busy so that another developer in Red Hat, Bastien Nocera, took this project instead. He moved the libfprint git repo to freedesktop.org, with the newest patches applied. You can find the new git repo <a href="http://cgit.freedesktop.org/libfprint/libfprint/" target="_blank">here</a>, and new libfprint release(0.2.0) <a href="http://freedesktop.org/~hadess/libfprint-0.2.0.tar.bz2" target="_blank">here</a>. Bastien Nocera is also maintaining <a href="http://www.reactivated.net/fprint/wiki/Fprintd" target="_blank">fprintd</a>, "a D-Bus daemon that offers libfprint functionality over the D-Bus interprocess communication bus". The git repo and new release are located <a href="http://cgit.freedesktop.org/libfprint/fprintd/" target="_blank">here</a> and <a href="http://freedesktop.org/~hadess/fprintd-0.2.0.tar.bz2" target="_blank">here</a>.</p>

<p>Besides the RHBZ mentioned above, you should be able to get more information from this <a href="http://lists.reactivated.net/pipermail/fprint/2010-August/001530.html" target="_blank">message</a> in fprint mailing list.</p>

<p>I updated my gentoo overlay in github, too. You can emerge libfprint and fprintd after you synchronized <a href="http://github.com/casparant/caspar-gentoo" target="_blank">this</a> overlay.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Enable Fingerprint in Thinkpad T410 Under Gentoo]]></title>
    <link href="http://www.casparzhang.com/blog/2010/08/06/enable-fingerprint-in-thinkpad-t410-under-gentoo/"/>
    <updated>2010-08-06T00:00:00+08:00</updated>
    <id>http://www.casparzhang.com/blog/2010/08/06/enable-fingerprint-in-thinkpad-t410-under-gentoo</id>
    <content type="html"><![CDATA[<p>As I mentioned in the <a href="http://www.casparzhang.com/blog/2010/07/02/back-to-gentoo-again/" target="_blank">previous post</a>, I couldn't get my fingerprint reader work in Gentoo. Now this problem has been solved. ;-)</p>

<p>Before starting, here're some useful links may help those who are suffering from similar fingerprint issues like me:</p>

<p>If you're using a fingerprint with USB id "147e:2016", you can have a look at <a href="http://www.thinkwiki.org/wiki/Integrated_Fingerprint_Reader" target="blank">this thinkwiki link</a>, The "147e:2016" item is updated recently: a Red Hat Bugzilla URL added.</p>

<p>This <a href="https://bugzilla.redhat.com/show_bug.cgi?id=504399" target="blank">RHBZ</a> link tried to enable TCRD4C devices, but seemed that my fingerprint was another different type since it still did not work after applied all the patches attached.</p>

<p>------>8------ Cutting, Let's enable T410 fingerprint ------>8------</p>

<p>Using <a href="http://www.n-view.net/Appliance/fingerprint/" target="_blank">Fingerprint-GUI</a> and <a href="http://www.upek.com/solutions/pc_and_networking/sdks/linux/" target="_blank">UPEK SDK</a> can easily enable fingerprint reader in T410. Fingerprint-GUI provides an integration of setup GUI, authentication plugin, PAM module and the UPEK library, all of the GUI parts are written with QT4 (I don't like it :-\), released under GPL-v3 licence. However, UPEK SDK library is EULA copyrighted and seems never to release the code to us.</p>

<p><!--more-->Notice that you still need have <a href="http://reactivated.net/fprint/wiki/Libfprint" target="_blank">libfprint</a> installed, what's more, a version >= 1.0_pre is necessary, according to the manual of Fingerprint.</p>

<p>Since A PAM module is integrated in Fingerprint-GUI so that your fingerprint driver can work in GDM, sudo, gnome-screensave, etc, you must remove other fingerprint PAM modules like pam_fprint or pam_thinkfinger.</p>

<p>For Gentoo users, I have finished two ebuilds for this application. You can find them in <a href="http://github.com/casparant/caspar-gentoo" target="_blank">my github repo</a>, fprint is also included in the repo.</p>

<p>I'm not good at writing ebuilds, any suggestions are welcomed.</p>

<p>P.S. Is there anybody know how to assert the existence of a kernel module in a ebuild? I need to judge whether `uinput.ko' exists or not among current kernel modules. Thanks.
</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[重新装回 Gentoo]]></title>
    <link href="http://www.casparzhang.com/blog/2010/07/02/back-to-gentoo-again/"/>
    <updated>2010-07-02T00:00:00+08:00</updated>
    <id>http://www.casparzhang.com/blog/2010/07/02/back-to-gentoo-again</id>
    <content type="html"><![CDATA[<p>最近到手 T410，先是折腾了几天把里面的正版 Win7 Pro 通过无损分区移到角落里，然后是装了个 RHEL6.0 用来做测试，最后重新开始折腾 Gentoo。</p>

<p>这次没有纠结于选择哪个桌面环境上，我一点也不嫌弃 GNOME 的臃肿，于是装了个 gnome-light。USE Flag 把以前用过的拿过来修改了一下，顺便提一句，在我不用 Gentoo 的这段时间听说 hal 被干掉了，所以我也追风把 hal 标记给去掉了。</p>

<p>启动的时候使用 grub2，本来打了 915resolution 补丁来使 grub 宽屏显示，但是打的补丁和 grub-1.98 似乎不是特别搭配，导致启动 grub 前后各有 5 秒停滞时间，加上 grub 启动过后仍然会有一个分辨率只有 640x480 的屏幕，我干脆就不用 915resolution 补丁了。使用 grub2，配上 ext4 分区，就可以不使用 initramfs 了，关键在两点，一是内核中 ACHI 选项要编译进内核，二是 kernel 行参数中不要使用 UUID。</p>

<p>说一下一些 Thinkpad 相关的配置：</p>

<p>一是声卡驱动。Thinkpad T410 的声卡应该是 CONEXANT 的，但是选择这个驱动后内核还是不认我的声卡。最后只好选择了通用驱动。</p>

<p>二是 hdaps，所谓的硬盘防震保护系统。portage 里面相关的三个包，一个是 hdaps 模块，一个是 GNOME 的 Applet，还有一个是 OpenGL 绘制的小程序，用来展示你的笔记本目前的水平状态。</p>

<p><!--more-->三是 Thinkpad 相关的快捷键。目前键盘上展示的快捷键基本上都能用，除了 Fn+F6 的“摄像头、耳机”、Fn+F8 的“触摸板、小红点切换”还有麦克风静音三个按钮不能工作。根据 Thinkwiki 的<a href="http://www.thinkwiki.org/wiki/How_to_get_special_keys_to_work" target="_blank">这里</a>说，Fn+F8 可以通过 thinkpad-acpi 或者 tps 使之工作，其他两个目前还没办法。不过我连 Fn+F8 都没搞定，只是自己写了个脚本，用来切换触摸板的开关（用小红点的时候肯定很讨厌触摸板开着）：</p>

<p><pre lang="bash">
#!/bin/bash</pre></p>

<p>status=`synclient -l | grep TouchpadOff | awk '{print $3}'`<br />
if [ $status -eq 0 ]<br />
then<br />
    synclient TouchpadOff=1<br />
    notify-send "Touchpad is disabled!"<br />
elif [ $status -eq 1 ]<br />
then<br />
    synclient TouchpadOff=0<br />
    notify-send "Touchpad is enabled"<br />
fi

把它保存为脚本，加上 x 权限，通过 GNOME 的键盘快捷键来控制，比如 Win+Space。</p>

<p>另外我给蓝牙设置了一个快捷键。笔记本上那个蓝色的 ThinkVantage 按钮没用到，就把它绑定了一个 ACPI 事件，设置为给蓝牙控制开关。事件代码如下：</p>

<p><pre lang="bash">
event=ibm/hotkey HKEY 00000080 00001018
action=/home/caspar/.local/bin/bluetooth.sh
</pre></p>

<p>上述代码保存为 ACPI 事件文件，如/etc/acpi/events/bluetooth。快捷键对应的 KEY CODE 可以到 Thinkwiki 上面去找，也可以看内核的:Documentation/laptop/thinkpad-acpi.txt 文档。至于用于控制的 bluetooth.sh 脚本，则如下(代码来源仍旧是 ThinkWiki，看<a href="http://www.thinkwiki.org/wiki/How_to_setup_Bluetooth" target="_blank">这里</a>)：</p>

<p><pre lang="bash">
#!/bin/bash

NAME="tpacpi_bluetooth_sw"

find_by_name() {
    NAME="$1"
    test -z "${NAME}" && return 1
    SYSFS_NAME=$(find -P /sys -name name -path "*/rfkill/*" -exec grep -l "${NAME}" \{\} + | head -n 1)
    if [[ ! -z "${SYSFS_NAME}" && -r "${SYSFS_NAME/%name/state}" ]]; then
        RESULT=${SYSFS_NAME%%/name}
        echo $RESULT
        return 0
    fi
    return 1
}

SYSFS="$(find_by_name "${NAME}")"

if [[ ! -z "${SYSFS}" && -r "${SYSFS}/state" ]]; then
    case "$(cat "${SYSFS}/state")" in
        0)
            echo 1 > "${SYSFS}/state"
            ;;
        1)
            echo 0 > "${SYSFS}/state"
            ;;
    esac
fi
</pre></p>

<p>重启 ACPI 服务就可以看到蓝牙可以被 ThinkVantage 按钮控制了。</p>

<p>目前还不能工作的设备有：指纹识别器，从 lshw 中看到似乎还有 HECI 控制器(Intel 似乎不想管这个咚咚了？）、温度检测设备(Thermal Subsystem)和一个 Ricoh 的外围设备。</p>

<p>最后向使用 Thinkpad 的同学严重推荐<a href="http://www.thinkwiki.org" target="_blank">ThinkWiki</a>。</p>

<p>P.S. 设置充电阈值：据说如果电池不怎么经常使用的话，充电不充满有利于延长电池寿命（Win7 下的配套软件是这么说的），所以可以设置一个充电阈值，把下面命令放到/etc/conf.d/local.start 去：</p>

<p><pre lang="bash">echo 95 > /sys/devices/platform/smapi/BAT0/stop_charge_thresh</pre></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Ya-bupt-thesis-bachelor 1.0 Alpha 1 Released]]></title>
    <link href="http://www.casparzhang.com/blog/2010/05/16/ya-bupt-thesis-bachelor-1-0-alpha-1-released/"/>
    <updated>2010-05-16T00:00:00+08:00</updated>
    <id>http://www.casparzhang.com/blog/2010/05/16/ya-bupt-thesis-bachelor-1-0-alpha-1-released</id>
    <content type="html"><![CDATA[<p>这个东西总算是做了一大半了。因为拖了两个星期，论文还没开始写，接下来只能一边写论文一边改模板了。</p>

<p>简要介绍一下这个模板。</p>

<p>缘起是@yegle 曾经推荐过他的同学梵高(cnMuggle)的一个<a href="http://code.google.com/p/buptthesis-bachelor/" target="_blank">北邮本科毕业论文模板</a>，我看了一下觉得不太符合我的需求，最主要是他用了 CTeX，这玩意儿在我的机子上水土不服。作为一个 Linux+XeTeX+xeCJK 党，我就自己动手开始写。</p>

<p>期间参考了梵高同学的模板，当然也参考了<a href="http://wangxu.me" target="_blank">@gnawux(apt)</a>同学<a href="http://code.google.com/p/latex-bupt/" target="_blank">的</a>，还有 DazzleZhang 在 apt 的基础上改进<a href="http://code.google.com/p/buptthesis/" target="blank">的</a>，还有老牌的<a href="http://sourceforge.net/projects/thuthesis/" target="_blank">ThuThesis</a>。当然最主要还是依靠 Google 和 CTeX 论坛，把一些稀奇古怪的问题给解决了。</p>

<p>今天又解决了几个大问题，同时也把插入图片、插入表格、插入公式的格式整理了一下，所以就发布 Alpha-1 版本了。地址在<a href="http://code.google.com/p/yet-another-bupt-thesis-bachelor/" target="_blank">Google Code</a>上。接下来要完成的主要工作就是参考文献的排版。我一看其他模板的 bst 文件，几千行的代码，吓坏了。下次再搞好了。</p>

<p>欢迎提 Bug~</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[当系统调用不存在……]]></title>
    <link href="http://www.casparzhang.com/blog/2010/04/07/when-the-system-call-does-not-exist/"/>
    <updated>2010-04-07T00:00:00+08:00</updated>
    <id>http://www.casparzhang.com/blog/2010/04/07/when-the-system-call-does-not-exist</id>
    <content type="html"><![CDATA[<p>最近在一个项目上卡了几天，整理出来主要碰到的问题，以备自己查阅。</p>

<p>具体的问题抽象出来就是这么一种情况：</p>

<p>有一堆分层的目录，里面全是系统调用的相关测试代码+Makefile，结构大致如下：</p>

<p><blockquote>
├── wait4<br />
│   ├── Makefile<br />
│   ├── wait401.c<br />
│   └── wait402.c<br />
├── waitid<br />
│   ├── waitid01<br />
│   │   ├── Makefile<br />
│   │   └── waitid01.c<br />
│   ├── waitid02<br />
│   │   ├── Makefile<br />
│   │   └── waitid02.c<br />
├── waitpid<br />
│   │   ├── Makefile<br />
│   │   └── waitpid_err_test.c<br />
.....</blockquote></p>

<p><!--more-->现在要把这堆系统调用的测试代码移植到各硬件平台和各发行版上，因此要考虑内核版本、glibc 版本等等问题。比如说 getcpu 这个东东只在 2.6.19 及以后版本中存在并且只有 x86_64 and i386 架构才可用。而编译这堆系统调用使用的 Makefile 大致如下：</p>

<p><pre lang="bash" line="1">
MAKEFILES_FOR_TESTCASES = $(shell find testcases -name Makefile)
TESTCASES_BY_MAKE = $(addsuffix /test,$(dir $(MAKEFILES_FOR_TESTCASES)))</pre></p>

<p>$(TESTCASES_BY_MAKE):<br />
        $(MAKE) -C $(dir $@) test
</p>

<p>因此可能的解决方案有如下几种：</p>

<p><h4>1. 修改 Makefile，禁止编译会出错的测试用例</h4></p>

<p><pre lang="bash" line="1">
FILTER_OUT_CASES = testcase1 testcase2 testcase3
ALL_MAKEFILES = $(shell find testcases -name Makefile)
FILTER_OUT_MAKEFILES = $(wildcard $(foreach filename,$(FILTER_OUT_CASES),testcases/$(filename)/Makefile))
MAKEFILES_FOR_TESTCASES = $(filter-out $(FILTER_OUT_MAKEFILES),$(ALL_MAKEFILES))
TESTCASES_BY_MAKE = $(addsuffix /test,$(dir $(MAKEFILES_FOR_TESTCASES)))</pre></p>

<p>$(TESTCASES_BY_MAKE):<br />
        $(MAKE) -C $(dir $@) test
</p>

<p>代码很平淡无奇，之所以贴出这段代码是为了温习一下 Makefile 中 wildcard,filter-out 和 foreach 的用法 :-)</p>

<p><h4>2.修改 c 代码，使用预处理判断是否存在</h4></p>

<p>这种办法在<a href="ltp.sf.net/" target="_blank">ltp</a>上应用十分广泛，ltp 中自动从 configure 文件中生成 include/config.h，生成的 config.h 包含了一系列预处理，例如：</p>

<p><pre lang="cpp" line="1">
/* Define to 1 if you have the <sys/signalfd.h> header file. */
#undef HAVE_SYS_SIGNALFD_H</pre></p>

<p>/* Define to 1 if you have the <sys/stat.h> header file. */<br />
#define HAVE_SYS_STAT_H 1
</p>

<p>当内核版本不同导致系统调用不存在时，可以设置 undef 预处理；如果系统调用存在，则 define 一下。</p>

<p>接下去可以修改源代码，在可能会在不同内核版本上出现分歧的代码前后加上：</p>

<p><pre lang="cpp" line="1">#ifdef HAVE_SYS_SIGNALFD_H
...
#else
int main()
{
  printf("syscall not exists in this platform\");
  return 1;
}
#endif</pre></p>

<p>同时 ltp 还在 C 代码中判断内核版本，内核版本在<sys/utsname.h>中有声明。</p>

<p>如果不使用 configure 文件，可以自己写一个 shell 脚本来生成，例如：</p>

<p><pre lang="bash" line="1">
#!/bin/sh</pre></p>

<p>CONFIG_PATH=./include/config.h<br />
KVER=`uname -r | cut -d'-' -f 1`<br />
KMAJVER=`echo $KVER | cut -d'.' -f 1-2`<br />
KMINVER=`echo $KVER | cut -d'.' -f 3`</p>

<p>if [ "$KMAJVER" = "2.6" ] && [ $KMINVER -ge 19 ];<br />
then<br />
	echo '#define HAVE_SYS_EPOLL_H 1' >> $CONFIG_PATH<br />
else<br />
	echo '#ifdef HAVE_SYS_EPOLL_H' >> $CONFIG_PATH<br />
	echo '#  undef HAVE_SYS_EPOLL_H' >> $CONFIG_PATH<br />
	echo '#endif' >> $CONFIG_PATH<br />
fi

</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[生活在信息膨胀的社会]]></title>
    <link href="http://www.casparzhang.com/blog/2010/04/07/how-to-organize-your-information/"/>
    <updated>2010-04-07T00:00:00+08:00</updated>
    <id>http://www.casparzhang.com/blog/2010/04/07/how-to-organize-your-information</id>
    <content type="html"><![CDATA[<p>现在上网的，没下列东东都不好意思说自己是混互联网的：</p>

<p><blockquote>
    <a href="http://www.casparzhang.com">个人博客</a>（BSP/独立的至少得有一个吧），<br/>
    <a href="http://twitter.com/casparant" target="_blank">twitter</a>（以及其他各种围脖们），<br />
    <a href="http://www.douban.com/people/casparant/" target="_blank">豆瓣</a>，<br />
    <a href="http://www.facebook.com/casparant" target="_blank">facebook</a>，<br />
    <a href="https://www.google.com/reader/shared/casparant" target="_blank">Google Reader</a>（或者其他 rss 订阅工具），<br />
    <a href="http://feeds.feedburner.com/casparant" target="_blank">feedburner</a>/<a href="http://feed.feedsky.com/casparant" target="_blank">feedsky</a>，<br />
    <a href="http://www.flickr.com/photos/ant-veil/" target="_blank">flickr 相册</a>(或者 picasaweb,或者国内的 oo 和 xx），<br />
    <a href="http://cn.last.fm/user/casparant" target="_blank">lastfm</a>, <br />
    youtube 订阅，<br />
    friendfeed, <br />
    ……
</blockquote></p>

<p>在这个信息膨胀的社会，如何获取信息是个头疼的问题。下面列一下自己是如何把这些信息来源整合起来的，以供参考：</p>

<p>整合所有信息的核心就是 feed/rss，所以首先要把博客搞一个托管，或者干脆直接用自带的 feed（前者的好处是，换了博客可以不用通知别人修改你的 rss 地址）。我主要采用 feedburner，在 wordpress 里用了<a href="http://vonye.com/projects/feedred">feedred</a>这个插件，把自带的 feed 重定向到了 feedburner 上。因为很久很久以前 feedburner 被墙了，所以 feedsky 也用过。</p>

<p>然后就是 Twitter，作为一个重度 Twitter 患者，巴不得把自己博客更新、Google Reader 分享、豆瓣新鲜事、新拍的照片等一堆东西同步到 Twitter 上。这里有两种解决方案：</p>

<p>1. 使用<a href="http://twitterfeed.com">TwitterFeed.com</a></p>

<p>2. 使用 FeedBurner。</p>

<p>前者自己摸索就好了，我更推荐后者，因为自己有种感觉 FeedBurner 同步速度很快，莫非它采用了每分钟轮询的策略？Orz……在 FeedBurner 管理界面的"Publicize"中有个"Socialize"，里面可以设置把当前 feed 同步到 Twitter 上。</p>

<p><!--more-->因此只要你的信息源有 feed，就可以同步到 Twitter 上。</p>

<p>接下来是 facebook、校内这类社交网站，把 twitter 同步到 facebook/校内上。</p>

<p>校内的话，狗狗<a href="http://yegle.net/2009/04/21/use-bash-to-update-xiaonei-status-and-blog/" target="_blank">写过一个脚本</a>，请自行取用。facebook 的话，也有两种方式：</p>

<p>1. 使用 friendfeed（已被 facebook 收购鸟），</p>

<p>2.使用 facebook app</p>

<p>后者据<a href="http://twitter.com/luopf" target="_blank">lpf</a>同学反映，密码老是不对。我自己是很早很早以前设置的，忘了。建议有问题的同学使用第一种方案。friendfeed 因为被 facebook 收购了，也就更好地集成在了 facebook 中（不好意思我忘了怎么集成了，自己摸索一下吧>_<）。需要注意的是 friendfeed 除了可以从 twitter 同步，还可以从其他源同步，这些源可能会和你之前设定的 Twitter 集成重复。</p>

<p>其他的，比如同步到<a href="http://www.williamlong.info/archives/1962.html" target="_blank">新浪围脖</a>，请自行查阅。</p>

<p>还有什么需要补充，欢迎留言。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[搞定 LaTeX Beamer 模板]]></title>
    <link href="http://www.casparzhang.com/blog/2010/04/03/know-how-to-make-a-latex-beamer-template/"/>
    <updated>2010-04-03T00:00:00+08:00</updated>
    <id>http://www.casparzhang.com/blog/2010/04/03/know-how-to-make-a-latex-beamer-template</id>
    <content type="html"><![CDATA[<p>过一段时间公司和学校都得做 presentation，所以现在就得好好开始准备 slides。以前我就用 Beamer 做过一次 slide，而且是匆匆忙忙用了默认模板搞定的。这次借着再做 slides 的机会，我得自己搞个模板。</p>

<p>需求如下，非常简单：</p>

<p><blockquote>1. 要在合适的位置打上 Logo（一般是左上角或者右上角）;<br />
2. 要能自定义页脚<br />
3. 首页要有背景</blockquote></p>

<p>经过半个白天加一个晚上的折腾，基本上解决了上述问题：</p>

<p>要打 Logo，并且自定义位置，可以使用如下语句：</p>

<p><pre lang="latex"> \logo{\pgfputat{\pgfxy(#1,#2)}{\pgfbox[center,base]{\includegraphics[height=0.5cm]{#3}}}}</pre></p>

<p>事先要包含 pgf 宏包，#1,#2 是 Logo 坐标，#3 是 Logo 路径。关于坐标，我发现用 pdflatex 编译和用 xelatex 编译时坐标表示的实际位置是不同的，反正自己手工慢慢调就是了。也可以把上述代码加个\newcommand，做成一个带参数的命令直接调用。</p>

<p>自定义页脚，我参考了 beamer guide 和 beamer 里面的某个宏包的写法：</p>

<p><!--more--><pre lang="latex">
%% Custom footer
\usefoottemplate{
    \hbox{\tinycolouredline{structure}{
    \begin{beamercolorbox}[wd=0.22\paperwidth,ht=2.4ex,dp=1.2ex,left]{author in head/foot}%
        \usebeamerfont{author in head/foot}\insertshortauthor
    \end{beamercolorbox}%
    \begin{beamercolorbox}[wd=0.5\paperwidth,ht=2.4ex,dp=1.2ex,center]{title in head/foot}%
        \usebeamerfont{title in head/foot}\insertshorttitle
    \end{beamercolorbox}%
    \begin{beamercolorbox}[wd=0.28\paperwidth,ht=2.4ex,dp=1.2ex,right]{date in head/foot}%
        \usebeamerfont{date in head/foot}\insertshortdate{}\hspace*{0.8em}
        \insertframenumber{} / \inserttotalframenumber\hspace*{4.5ex}
    \end{beamercolorbox}}}%
}
</pre></p>

<p>需要说明的是，我始终搞不懂\tinycolouredline 的第一个参数 structure 还能用什么代替，而且上述方案有个及其大的 BUG，在页脚最左边的那列的左边有 1em 的空白，如果要给这列填充了和那个空白区与不一样的背景色，就会很难看。我也没找到消除那列空白的代码。</p>

<p>首页加背景就太简单了，我直接用 wallpaper 宏包，然后在第一页加入：</p>

<p><pre lang="latex">\ThisCenterWallPaper{1}{inc/cover}</pre></p>

<p>不管是加 Logo 也好，加墙纸也好，它们都很容易被 colortheme 中定义的背景色覆盖，这可以通过\setbeamercolor 来去掉背景色，基本语法如下：</p>

<p><pre lang="latex">\setbeamercolor{background canvas}{bg=}
\setbeamercolor{title}{fg=black, bg=lightgray!80!white}</pre></p>

<p>最后放出模板，<a href="https://docs.google.com/leaf?id=0B-GDvj9JsmanZjUzZDI4M2MtYzE3MS00YTNjLWEyZjUtYjFhMzY5MzY5NWVi&hl=zh_CN">点此下载>>>></a></p>
]]></content>
  </entry>
  
</feed>
