<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Soul Of Free Loop &#187; kernel</title>
	<atom:link href="https://zohead.com/archives/tag/kernel/feed/" rel="self" type="application/rss+xml" />
	<link>https://zohead.com</link>
	<description>Uranus Zhou&#039;s Blog</description>
	<lastBuildDate>Sat, 19 Jul 2025 15:42:46 +0000</lastBuildDate>
	<language>zh-CN</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.8</generator>
	<item>
		<title>使用Software Collections编译最新Linux kernel</title>
		<link>https://zohead.com/archives/scl-linux-kernel/</link>
		<comments>https://zohead.com/archives/scl-linux-kernel/#comments</comments>
		<pubDate>Thu, 07 Sep 2017 16:08:52 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[binutils]]></category>
		<category><![CDATA[CentOS]]></category>
		<category><![CDATA[gcc]]></category>
		<category><![CDATA[kernel]]></category>
		<category><![CDATA[Software Collections]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1502</guid>
		<description><![CDATA[关于 Software Collections 使用 Software Collections（SCL）的契机是最近我需要在 CentOS 6 系统上编译最新版本的 Linux kernel，主要用于测试 Btrfs 的数据校验对于 AVX2 指令的支持情况。然而大家都知道 CentOS 6 自带的软件包版本都是非常老的，glibc 维持在 2.12 版本，老的 gcc 4.4 版本则根本就不支持 AVX2 指令，这样编译 Linux kernel 时会自动关闭 async xor 对 AVX2 的支持。 如果要自己动手编译新版本 gcc，这感觉还是有点吃力不讨好，幸好 Red Hat 也注意 [&#8230;]]]></description>
				<content:encoded><![CDATA[<h2 id="about-scl">关于 Software Collections</h2>
<p>使用 Software Collections（SCL）的契机是最近我需要在 CentOS 6 系统上编译最新版本的 Linux kernel，主要用于测试 Btrfs 的数据校验对于 AVX2 指令的支持情况。然而大家都知道 CentOS 6 自带的软件包版本都是非常老的，glibc 维持在 2.12 版本，老的 gcc 4.4 版本则根本就不支持 AVX2 指令，这样编译 Linux kernel 时会自动关闭 async xor 对 AVX2 的支持。</p>
<p>如果要自己动手编译新版本 gcc，这感觉还是有点吃力不讨好，幸好 Red Hat 也注意到这个问题了，我们可以通过 Software Collections 实现在一个系统上安装多个版本的开发工具或者运行环境，仓库里高版本的软件包通常都是由 Red Hat 维护的，比较常见的需求例如将 CentOS 6 自带的 Python 升级为 2.7 版本。</p>
<p>大家可以访问 Software Collections 官网了解更多信息：</p>
<p><a href="https://www.softwarecollections.org/en/" target="_blank">https://www.softwarecollections.org/en/</a></p>
<h2 id="install-scl-gcc">安装 gcc 新版本</h2>
<p>首先需要安装 SCL 支持，CentOS 6 通过 yum 安装还是非常简单的：</p>
<pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# yum install centos-release-scl
Dependencies Resolved

================================================================
 Package              Arch    Version            Repository Size
================================================================
Installing:
 centos-release-scl   noarch  10:7-3.el6.centos  extras     12 k

Transaction Summary
================================================================
Install       1 Package(s)

Total download size: 12 k
Installed size: 19 k
Is this ok [y/N]: y
Downloading Packages:
centos-release-scl-7-3.el6.centos.noarch.rpm | 12 kB    00:00     
Running rpm_check_debug
Running Transaction Test
Transaction Test Succeeded
Running Transaction
  Installing : 10:centos-release-scl-7-3.el6.centos.noarch
duration: 120(ms)
Installed products updated.

Installed:
  centos-release-scl.noarch 10:7-3.el6.centos                               

Complete!
</pre>
<p>然后安装 SCL 工具：</p>
<pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# yum install scl-utils scl-utils-build
</pre>
<p>运行 <code>yum makecache</code> 更新缓存之后，就可以查找 SCL 提供了哪些新版本的 gcc 和 binutils 包了：</p>
<pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# yum list | grep -E &quot;devtoolset-.*-(gcc|binutils)&quot;
devtoolset-3-binutils.x86_64                2.24-18.el6           centos-sclo-rh
devtoolset-3-binutils-devel.x86_64          2.24-18.el6           centos-sclo-rh
devtoolset-3-gcc.x86_64                     4.9.2-6.2.el6         centos-sclo-rh
devtoolset-3-gcc-c++.x86_64                 4.9.2-6.2.el6         centos-sclo-rh
devtoolset-3-gcc-gfortran.x86_64            4.9.2-6.2.el6         centos-sclo-rh
devtoolset-3-gcc-plugin-devel.x86_64        4.9.2-6.2.el6         centos-sclo-rh
devtoolset-4-binutils.x86_64                2.25.1-8.el6          centos-sclo-rh
devtoolset-4-binutils-devel.x86_64          2.25.1-8.el6          centos-sclo-rh
devtoolset-4-gcc.x86_64                     5.3.1-6.1.el6         centos-sclo-rh
devtoolset-4-gcc-c++.x86_64                 5.3.1-6.1.el6         centos-sclo-rh
devtoolset-4-gcc-gdb-plugin.x86_64          5.3.1-6.1.el6         centos-sclo-rh
devtoolset-4-gcc-gfortran.x86_64            5.3.1-6.1.el6         centos-sclo-rh
devtoolset-4-gcc-plugin-devel.x86_64        5.3.1-6.1.el6         centos-sclo-rh
devtoolset-6-binutils.x86_64                2.27-8.el6            centos-sclo-rh
devtoolset-6-binutils-devel.x86_64          2.27-8.el6            centos-sclo-rh
devtoolset-6-gcc.x86_64                     6.2.1-3.1.el6         centos-sclo-rh
devtoolset-6-gcc-c++.x86_64                 6.2.1-3.1.el6         centos-sclo-rh
devtoolset-6-gcc-gdb-plugin.x86_64          6.2.1-3.1.el6         centos-sclo-rh
devtoolset-6-gcc-gfortran.x86_64            6.2.1-3.1.el6         centos-sclo-rh
devtoolset-6-gcc-plugin-devel.x86_64        6.2.1-3.1.el6         centos-sclo-rh
</pre>
<p>可以看到 SCL 提供了 4.9.2、5.3.1、6.2.1 这几个新版本的 gcc 支持，与 binutils 一起分别属于几个不同版本的 devtoolset 软件包。</p>
<p>初步确认 gcc 5.3.1 就可以支持 AVX2 指令，因此直接安装对应 devtoolset-4 版本的 gcc 和 binutils 软件包：</p>
<pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# yum install devtoolset-4-gcc.x86_64 devtoolset-4-binutils.x86_64
</pre>
<h2 id="use-scl">使用 SCL</h2>
<p>软件包安装完成之后，我们可以使用 <code>scl -l</code> 命令查看 SCL 安装的软件包列表：</p>
<pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# scl -l
devtoolset-4
python27
</pre>
<p>当然 SCL 并不会直接替换系统自带的老版本软件包，需要通过 scl 命令切换使用：</p>
<pre class="brush: bash; title: ; notranslate">
scl enable &lt;scl-package-name&gt; &lt;command&gt;
</pre>
<p>这样我想开启一个使用新版本 gcc 的 shell，直接运行命令：</p>
<pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# scl enable devtoolset-4 bash
</pre>
<p>这样会启动一个新的 bash shell，可以在其中看到 gcc 已经是新的 5.3.1 版本了：</p>
<pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# gcc --version
gcc (GCC) 5.3.1 20160406 (Red Hat 5.3.1-6)
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
</pre>
<p>当然如果不想开启新的 shell，也可以进入 Linux kernel 源代码目录直接使用新版本 gcc 来编译：</p>
<pre class="brush: bash; title: ; notranslate">
[root@localhost ~/linux-4.10]# scl enable devtoolset-4 make
</pre>
<p>最后提醒一下 Software Collections 的软件包是可以不安装直接在线搜索的哦，从 SCL 官网的 collections 里就可以搜索：</p>
<p><a href="https://www.softwarecollections.org/en/scls/" target="_blank">https://www.softwarecollections.org/en/scls/</a></p>
<p>搜索结果里就会有 SCL 软件包的安装方法，例如 Python 2.7 搜索到之后直接运行 <code>yum install python27</code> 就可以安装使用了，祝大家玩的开心。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/scl-linux-kernel/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>小内存OpenVZ VPS使用UML开启BBR</title>
		<link>https://zohead.com/archives/openvz-uml-bbr/</link>
		<comments>https://zohead.com/archives/openvz-uml-bbr/#comments</comments>
		<pubDate>Mon, 06 Mar 2017 18:39:55 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[User-mode Linux]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[网络]]></category>
		<category><![CDATA[BBR]]></category>
		<category><![CDATA[busybox]]></category>
		<category><![CDATA[kernel]]></category>
		<category><![CDATA[OpenVZ]]></category>
		<category><![CDATA[Shadowsocks]]></category>
		<category><![CDATA[ShadowsocksR]]></category>
		<category><![CDATA[TAP]]></category>
		<category><![CDATA[UML]]></category>
		<category><![CDATA[VPS]]></category>
		<category><![CDATA[翻墙]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1365</guid>
		<description><![CDATA[前言 Bottleneck Bandwidth and RTT（BBR）是 Google 最近提交到 Linux 4.9 版本内核的新 TCP 拥塞控制算法，BBR 相对于 Linux 中已经用了好多年的默认 CUBIC 算法的主要改进是在有一定丢包率的网络链路上充分利用带宽，另外也能有效降低延迟。这对于很多使用美帝 VPS + Shadowsocks 爬墙方案的人来说应该还是比较有用的，目前从一些更新了内核开启 BBR 的 KVM VPS 用户的反馈来看效果也还是不错的。 我目前使用的爬墙服务器是 AlphaRacks 的低配 OpenVZ VPS，内存只有 96 MB，到国内的线路质量倒还 [&#8230;]]]></description>
				<content:encoded><![CDATA[<h2 id="preface">前言</h2>
<p><a href="https://github.com/google/bbr">Bottleneck Bandwidth and RTT（BBR）</a>是 Google 最近提交到 Linux 4.9 版本内核的新 TCP 拥塞控制算法，BBR 相对于 Linux 中已经用了好多年的默认 CUBIC 算法的主要改进是在有一定丢包率的网络链路上充分利用带宽，另外也能有效降低延迟。这对于很多使用美帝 VPS + Shadowsocks 爬墙方案的人来说应该还是比较有用的，目前从一些更新了内核开启 BBR 的 KVM VPS 用户的反馈来看效果也还是不错的。</p>
<p>我目前使用的爬墙服务器是 AlphaRacks 的低配 OpenVZ VPS，内存只有 96 MB，到国内的线路质量倒还凑合，OpenVZ 虽然没办法直接升级 kernel ，但看到 AlphaRacks 控制面板里能开启 TUN/TAP，那可以考虑捣鼓使用 User-mode Linux（UML）来开启 BBR 了。</p>
<p>User-mode Linux 和 QEMU 这种虚拟机稍微有点类似，但 UML 是直接将 Linux 内核编译成可执行的 ELF 应用层程序，这样可以直接在普通 Linux 环境中运行及调试其它版本 Linux 内核的功能，有关 UML 的详细信息请参考其<a href="http://user-mode-linux.sourceforge.net/">官方网站</a>。</p>
<blockquote>
<p><strong>提示</strong></p>
<p>不想自己编译 UML 内核和构建 UML rootfs 的朋友可以直接拉到「<a href="#use-uml-ss-system">使用 UML Shadowsocks 系统</a>」这部分哦。</p>
</blockquote>
<h2 id="compile-uml-kernel">编译 User-mode Linux 内核</h2>
<h3 id="compile-prepare">准备编译环境</h3>
<p>我是直接在 Windows 10 RS2 64 位系统上编译 UML 内核的，使用 Win10 自带的 Bash on Ubuntu 14.04 环境编译还是非常方便的，而且编译速度相比使用虚拟机也是快了很多。</p>
<p>首先安装编译需要的软件包：</p>
<blockquote>
<p><strong>备注</strong></p>
<p>由于我使用的 OpenVZ VPS 系统是 32 位的，所以这里安装 <code>libc-dev</code> 和 <code>libvdeplug-dev</code> 开发包时都指定了使用 32 位版本；如果你需要编译 64 位版本的 UML 内核，那把下面的 <code>-i386</code> 去掉即可。</p>
</blockquote>
<pre class="brush: bash; title: ; notranslate">
zzm@ZZM-P238:~$ sudo apt-get install build-essential libncurses5-dev libc6-dev-i386 libvdeplug-dev-i386
</pre>
<p>这里安装 <code>libvdeplug-dev</code> 开发包是为了支持 UML 的 <a href="http://wiki.virtualsquare.org/wiki/index.php/VDE_Basic_Networking#Step_3:_run_a_User-Mode_Linux">Virtual Distributed Ethernet（VDE）</a>模式网卡设备，VDE 主要是为了让 UML 的网络功能在类似 Docker 这种连 TUN/TAP 都不支持的系统上也能工作起来。</p>
<p>接着可以下载最新 Linux 4.10.1 版本内核源代码，并解压缩准备配置内核选项，4.10.1 版本源代码解压之后不到 800 MB。</p>
<blockquote>
<p><strong>提示</strong></p>
<p>如果像我一样直接在 Win10 Bash 环境中编译，那么最好将内核源代码解压到 Win10 Bash 的 VolFs 文件系统中（也就是 Ubuntu rootfs 目录，一般直接 cd 到 <code>HOME</code> 主目录即可）。</p>
</blockquote>
<h3 id="uml-kernel-config">UML 内核配置</h3>
<p>如果使用默认的 UML 内核配置是比较简单的，直接下面两步即可，第二步内核配置界面里可以手工开启 BBR 功能：</p>
<pre class="brush: bash; title: ; notranslate">
zzm@ZZM-P238:~/linux-4.10.1$ cp arch/um/configs/i386_defconfig .config
zzm@ZZM-P238:~/linux-4.10.1$ make menuconfig ARCH=um
</pre>
<p>不过为了能编译出体积最小又能满足 BBR 爬墙需要的 kernel，我还是使用自己修改的最小配置，首先将下面的内容保存为 <code>mini.config</code> 文件（如果需要编译 64 位 kernel，那直接把开头的 <code>CONFIG_64BIT=n</code> 这一行去掉即可）：</p>
<pre class="brush: bash; title: ; notranslate">
zzm@ZZM-P238:~/linux-4.10.1$ cat mini.config
CONFIG_64BIT=n
CONFIG_BINFMT_ELF=y
CONFIG_BINFMT_SCRIPT=y
CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=n
CONFIG_HOSTFS=y
CONFIG_MCONSOLE=y
CONFIG_MAGIC_SYSRQ=y
CONFIG_KERNEL_STACK_ORDER=1
CONFIG_SWAP=n
CONFIG_SYSVIPC=y
CONFIG_EXPERT=n
CONFIG_EMBEDDED=n
CONFIG_SLUB_DEBUG=n
CONFIG_BLOCK=y
CONFIG_LBDAF=y
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
CONFIG_BLK_DEV_BSG=n
CONFIG_BLK_DEV=y
CONFIG_BLK_DEV_UBD=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_IOSCHED_DEADLINE=y
CONFIG_IOSCHED_CFQ=n
CONFIG_DEFAULT_IOSCHED=&quot;deadline&quot;
CONFIG_NETDEVICES=n
CONFIG_STDERR_CONSOLE=y
CONFIG_SSL=y
CONFIG_NULL_CHAN=y
CONFIG_PORT_CHAN=y
CONFIG_PTY_CHAN=y
CONFIG_TTY_CHAN=y
CONFIG_XTERM_CHAN=y
CONFIG_UNIX98_PTYS=y
CONFIG_EXT2_FS=y
CONFIG_PROC_FS=y
CONFIG_PROC_SYSCTL=y
CONFIG_TMPFS=y
CONFIG_SYSFS=y
CONFIG_SCHED_DEBUG=n
CONFIG_NET=y
CONFIG_UNIX=y
CONFIG_INET=y
CONFIG_TCP_CONG_ADVANCED=y
CONFIG_TCP_CONG_CUBIC=y
CONFIG_TCP_CONG_BBR=y
CONFIG_DEFAULT_BBR=y
CONFIG_DEFAULT_TCP_CONG=&quot;bbr&quot;
CONFIG_IPV6=n
CONFIG_SYN_COOKIES=y
CONFIG_NET_SCHED=y
CONFIG_NET_SCH_QFQ=y
CONFIG_NET_SCH_CODEL=y
CONFIG_NET_SCH_FQ_CODEL=y
CONFIG_NET_SCH_FQ=y
CONFIG_NET_SCH_FIFO=y
CONFIG_UML_NET=y
CONFIG_UML_NET_TUNTAP=y
CONFIG_UML_NET_VDE=y
CONFIG_DEBUG_KERNEL=n
CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1
</pre>
<p>简单说明一下上面的内核配置选项：</p>
<ul>
<li>启用了 UML 的 hostfs 及 mconsole 等很实用的功能；</li>
<li>支持 UML 的 ubd 块设备，也开启了 loop 设备支持，去掉了 ramdisk 等没什么用的其它设备支持；</li>
<li>支持 proc、sysfs、tmpfs 等虚拟文件系统，真实的文件系统只保留了 ext2 支持，需要 ext3 或者 ext4 支持的朋友请自行修改；</li>
<li>开启 BBR 并设置为默认拥塞控制算法，另外需要为 BBR 启用 Fair Queue 包调度器；</li>
<li>去掉了 IPv6 支持，去掉了所有的网卡设备驱动；</li>
<li>去掉了几乎所有的内核调试信息。</li>
</ul>
<p>然后使用 <code>mini.config</code> 生成新的内核配置文件：</p>
<pre class="brush: bash; title: ; notranslate">
zzm@ZZM-P238:~/linux-4.10.1$ make ARCH=um allnoconfig KCONFIG_ALLCONFIG=mini.config
</pre>
<h3 id="compile-kernel">编译内核</h3>
<p>内核选项配置完毕之后，编译就非常简单了：</p>
<pre class="brush: bash; title: ; notranslate">
zzm@ZZM-P238:~/linux-4.10.1$ make ARCH=um vmlinux -j4
</pre>
<p>这里我开启了 4 个并发编译（make 的 <code>-j4</code> 参数）支持，UML 的 vmlinux 可执行程序在我的笔记本上不到 4 分钟就编译完成了，最终编译出来的 vmlinux 文件大小大概为 3 - 4 MB。</p>
<p>你可以试着运行编译出来的 vmlinux 可执行程序查看编译时使用的内核配置：</p>
<pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# ./vmlinux --showconfig
</pre>
<h2 id="prepare-uml-rootfs">准备 UML rootfs</h2>
<h3 id="prepare-rootfs-ss-libev">准备 rootfs 和 shadowsocks-libev</h3>
<p>既然 UML 内核我都是按最小化编译的，那运行的 rootfs 自然不能用像 Ubuntu、CentOS 那种「庞大」的系统了，这里我直接使用最小化的 busybox rootfs + Shadowsocks 服务端程序和运行库的方式，除了 busybox 基础环境和 Shadowsocks 服务端程序基本没有添加任何其它东西。</p>
<p>最终我将 UML vmlinux 内核文件和 rootfs 目录一起压缩打包起来大小也不过才 3 MB，这样能非常方便地上传到 VPS 上和分享给其他人。当然缺点就是如果需要安装其它的软件那只能手工拷贝到 rootfs 目录中了。</p>
<p>具体 busybox rootfs 的制作方法这里我就不做详细说明了，感兴趣的朋友可以参考相关的文档了解一下，我分别制作了 32 位和 64 位的 busybox rootfs 目录。</p>
<p>另外为了减少 UML 系统的程序依赖和内存占用，Shadowsocks 服务端我选择的是 <a href="https://github.com/shadowsocks/shadowsocks-libev">shadowsocks-libev</a>，这里我也分别编译了最新 3.0.3 版本 32 位和 64 位的服务端程序。</p>
<p>32 位 shadowsocks-libev 我是在 32 位 Ubuntu 14.04 系统下编译的，依赖的动态库也是使用的 Ubuntu 14.04 自带的库文件；64 位 shadowsocks-libev 则是在 64 位 CentOS 6 系统下编译的，所以 64 位运行库版本和 32 位的不同。你问我为什么不用同一个系统？因为我刚好有这两个系统的 VPS 能用来编译，实在是懒的换了 ^_^。</p>
<h3 id="test-uml-rootfs">测试 UML rootfs</h3>
<p>由于 OpenVZ 系统内核不支持 loop 设备，不能挂载文件系统映像进行修改，这里我直接使用 UML 的 hostfs 将 OpenVZ 主机系统的一个目录设为 UML 系统的 rootfs。</p>
<p>现在 UML hostfs 并没有某些网友说的存在只读的限制，而且相比使用 loop 设备或者 ubd 文件系统这种方式，hostfs 并不需要预先产生一个很大的文件，主机也能直接修改 UML rootfs 目录内容，UML 内核也根本不需要支持 ext3、ext4 这种真实的文件系统，综合比较下来实在是没有什么理由不用 hostfs。</p>
<p>首先安装 UML 工具 <code>uml-utilities</code> 软件包，后面的 <code>tunctl</code> 和 <code>uml_mconsole</code> 等命令会用到：</p>
<pre class="brush: bash; title: ; notranslate">
root@zohead:~# apt-get install uml-utilities
</pre>
<p>接着可以配置 OpenVZ 系统上的 TUN/TAP 虚拟网卡和开启端口转发：</p>
<pre class="brush: bash; title: ; notranslate">
root@zohead:~# tunctl -u root
Set 'tap0' persistent and owned by uid 0
root@zohead:~# ifconfig tap0 192.168.0.3 up
root@zohead:~# echo 1 &gt; /proc/sys/net/ipv4/ip_forward
root@zohead:~# iptables -t nat -I POSTROUTING -o venet0 -j MASQUERADE
root@zohead:~# iptables -t nat -A PREROUTING -i venet0 -p tcp --dport 9443 -j DNAT --to-destination 192.168.0.4
root@zohead:~# iptables -t nat -A PREROUTING -i venet0 -p udp --dport 9443 -j DNAT --to-destination 192.168.0.4
</pre>
<blockquote>
<p><strong>备注</strong></p>
<p>上面的命令中假设 TUN/TAP 虚拟网卡 IP 地址为 <code>192.168.0.3</code>（可以自行修改），UML 系统网卡 IP 地址为 <code>192.168.0.4</code>（需要和 TUN/TAP 虚拟网卡地址在同一网段），只将 OpenVZ 主机系统上 <code>9443</code> 端口的 TCP 和 UDP 数据转发到 UML 系统网卡上。</p>
</blockquote>
<p>最后是启动 UML 系统的例子：</p>
<pre class="brush: bash; title: ; notranslate">
root@zohead:~# ./vmlinux-i686 umid=shadowsocks root=/dev/root rootfstype=hostfs rootflags=/root/umlfs-i686 rw mem=48M init=/sbin/init eth0=tuntap,tap0
</pre>
<p>简单说明一下 UML vmlinux 程序的参数：</p>
<ul>
<li><code>umid</code> 参数指定 UML 系统 ID，建议每个 UML 系统都使用的固定的 ID，不然每次运行都会在 <code>~/.uml</code> 目录下产生新的临时目录；</li>
<li><code>rootflags</code> 参数指定 UML rootfs 路径，这里 <strong>必须</strong> 使用绝对路径；</li>
<li><code>mem</code> 参数指定 UML 系统的内存大小，经过我的测试，只运行 shadowsocks-libev 服务端的话就算分配 32 MB 内存看起来也没什么压力；</li>
<li><code>init</code> 参数指定 UML 系统的 init 进程，不建议修改；</li>
<li><code>eth0=tuntap,tap0</code> 则是指定 UML 的 tuntap 模式网卡，使用 OpenVZ 主机系统的 <code>tap0</code> 虚拟网卡设备。</li>
</ul>
<h2 id="use-uml-ss-system">使用 UML Shadowsocks 系统</h2>
<h3 id="uml-linux-sh">uml-linux.sh 脚本</h3>
<p>为了使用起来方便，我把启动 UML 系统和设置端口转发的操作写成了一个 <code>uml-linux.sh</code> 脚本，这样 OpenVZ 系统启动之后可以根据配置自动运行并配置端口转发：</p>
<pre class="brush: bash; title: ; notranslate">
#!/bin/sh
UML_ID=&quot;shadowsocks&quot;
TAP_DEV=&quot;tap0&quot;
ROUTER_DEV=&quot;venet0&quot;
TAP_ADDR=&quot;192.168.0.3&quot;
UML_ADDR=&quot;192.168.0.4&quot;
REV_TCP_PORTS=&quot;&quot;
REV_UDP_PORTS=&quot;&quot;
FWD_TCP_PORTS=&quot;9443&quot;
FWD_UDP_PORTS=&quot;9443&quot;

DIR=&quot;$( cd &quot;$( dirname &quot;$0&quot; )&quot; &amp;&amp; pwd )&quot;

[ ! -d /sys/class/net/$TAP_DEV ] &amp;&amp; tunctl -t $TAP_DEV
ifconfig $TAP_DEV $TAP_ADDR up

echo 1 &gt; /proc/sys/net/ipv4/ip_forward

iptables -t nat -I POSTROUTING -o $ROUTER_DEV -j MASQUERADE

for i in $REV_TCP_PORTS; do
	if [ &quot;x$i&quot; = &quot;x*&quot; ]; then
		P_TMP=&quot;&quot;
	else
		P_TMP=&quot;--dport $i &quot;
	fi
	iptables -t nat -A PREROUTING -p tcp $P_TMP-j RETURN
done
for i in $REV_UDP_PORTS; do
	if [ &quot;x$i&quot; = &quot;x*&quot; ]; then
		P_TMP=&quot;&quot;
	else
		P_TMP=&quot;--dport $i &quot;
	fi
	iptables -t nat -A PREROUTING -p udp $P_TMP-j RETURN
done

for i in $FWD_TCP_PORTS; do
	if [ &quot;x$i&quot; = &quot;x*&quot; ]; then
		P_TMP=&quot;&quot;
	else
		P_TMP=&quot;--dport $i &quot;
	fi
	iptables -t nat -A PREROUTING -i $ROUTER_DEV -p tcp $P_TMP-j DNAT --to-destination $UML_ADDR
done
for i in $FWD_UDP_PORTS; do
	if [ &quot;x$i&quot; = &quot;x*&quot; ]; then
		P_TMP=&quot;&quot;
	else
		P_TMP=&quot;--dport $i &quot;
	fi
	iptables -t nat -A PREROUTING -i $ROUTER_DEV -p udp $P_TMP-j DNAT --to-destination $UML_ADDR
done

nohup $DIR/vmlinux-i686 umid=$UML_ID root=/dev/root rootfstype=hostfs rootflags=$DIR/umlfs-i686 rw mem=48M init=/sbin/init con=pts eth0=tuntap,$TAP_DEV,,$UML_ADDR &amp;
</pre>
<p>一般只要修改脚本最开始的几行配置即可（最后一行 vmlinux 程序和 UML rootfs 路径默认使用脚本所在路径，如果不同请自行修改）：</p>
<ul>
<li><code>TAP_ADDR</code> 为 TUN/TAP 虚拟网卡地址，<code>UML_ADDR</code> 为 UML 系统内网卡地址；</li>
<li><code>REV_TCP_PORTS</code> 和 <code>REV_UDP_PORTS</code> 分别指定 OpenVZ 主机系统需要保留哪些 TCP 和 UDP 端口不转发到 UML 系统（多个以空格隔开），一般如果 UML 系统使用固定的一些端口，那么默认留空即可；</li>
<li><code>FWD_TCP_PORTS</code> 和 <code>FWD_UDP_PORTS</code> 分别指定哪些 TCP 和 UDP 端口需要转发到 UML 系统（默认只写了一个 <code>9443</code>，实际如果需要多个则以空格隔开），如果需要指定端口范围也可以写成 <code>15000:20000</code>，如果需要所有端口都转发到 UML 系统那可以直接写成 <code>*</code>（此时 <code>REV_TCP_PORTS</code> 一般最少要留一个端口用于 SSH 远程登录）；</li>
<li>需要分配给 UML 系统的内存大小请自行修改 vmlinux 的 <code>mem</code> 参数。</li>
</ul>
<h3 id="uml-ss-config">UML Shadowsocks 系统配置</h3>
<p>另外 UML 系统内部的网卡地址配置及启动 shadowsocks-libev 服务端的操作都在 <code>/etc/rc.local</code> 脚本中：</p>
<pre class="brush: bash; title: ; notranslate">
#!/bin/sh
if [ -d /sys/class/net/eth0 ]; then
	ifconfig eth0 192.168.0.4 up
	route add default gw 192.168.0.3 dev eth0
	sleep 2
fi

SS_DNS=&quot;&quot;

[ -f /etc/default/pdnsd ] &amp;&amp; . /etc/default/pdnsd

# start local DNS server if needed
if [ &quot;x$START_DAEMON&quot; = &quot;xyes&quot; -o &quot;x$START_DAEMON&quot; = &quot;xYES&quot; ]; then
	pdnsd -d
	[ $? -eq 0 ] &amp;&amp; SS_DNS=&quot;-d 127.0.0.1 &quot;
fi

for i in `ls /etc/shadowsocks-libev/*.json 2&gt;/dev/null`; do
	SS_NAME=`basename $i .json 2&gt;/dev/null`
	start-stop-daemon -n shadowsocks-$SS_NAME -x ss-server -b -S -- -c $i -u $SS_DNS--fast-open
done

exit 0
</pre>
<p>简单说明一下：</p>
<ul>
<li>这里的 <code>192.168.0.4</code> 网卡 IP 地址就对应上面的 <code>UML_ADDR</code>，<code>192.168.0.3</code> 网关地址就是 <code>TAP_ADDR</code>，因此如果上面脚本中的地址修改了，那么 UML rootfs 的 <code>/etc/rc.local</code> 中的地址也需要修改；</li>
<li>你可以将 shadowsocks-libev 配置文件放到 <code>/etc/shadowsocks-libev</code> 目录中，此脚本会自动遍历并一一启动 <code>ss-server</code> 服务器，同时默认也开启了 TCP Fast Open 和 UDP 转发；</li>
<li>另外 rootfs 里集成了 <code>pdnsd</code> DNS 服务器，如果需要实现 DNS 缓存加速或者 UML 系统的 UDP DNS 解析存在问题，都可以启用 <code>pdnsd</code> 本地 DNS 服务器。</li>
</ul>
<p>配置修改完成之后，运行 <code>uml-linux.sh</code> 脚本就会在后台启动 UML 系统，并根据需要自动启动 shadowsocks-libev 服务器。</p>
<h3 id="control-uml-system">控制 UML 系统</h3>
<p>如果你需要登录到 UML 系统，可以先用 <code>uml_mconsole</code> 命令得到 UML 系统分配到的虚拟终端（第二个参数就是 UML 系统 ID）：</p>
<pre class="brush: bash; title: ; notranslate">
root@zohead:~# uml_mconsole shadowsocks config con0
OK pts:/dev/pts/2
</pre>
<p>然后使用 <code>screen</code> 命令就可以连接到该虚拟终端上（默认什么都不显示，需要按回车键终端才会出来）：</p>
<pre class="brush: bash; title: ; notranslate">
root@zohead:~# screen /dev/pts/2
</pre>
<p>如果想在 OpenVZ 主机系统里直接关闭整个 UML 系统，那也非常简单：</p>
<pre class="brush: bash; title: ; notranslate">
root@zohead:~# uml_mconsole shadowsocks halt
</pre>
<h3 id="uml-ss-download">UML Shadowsocks 系统下载</h3>
<p>为了方便大家使用，我把 32 位和 64 位的 UML 4.10.1 版本 vmlinux 内核程序及配置和 Shadowsocks 服务端 rootfs 目录、<code>uml-linux.sh</code> 脚本一起打包分享给大家。</p>
<p>32 位的 UML Shadowsocks 系统（百度网盘备用）：</p>
<p><a href="https://zohead.com/downloads/uml-shadowsocks-i686.tar.xz">https://zohead.com/downloads/uml-shadowsocks-i686.tar.xz</a> <br />
<a href="https://pan.baidu.com/s/1kVdoWcB" target="_blank">https://pan.baidu.com/s/1kVdoWcB</a></p>
<p>64 位的 UML Shadowsocks 系统：</p>
<p><a href="https://zohead.com/downloads/uml-shadowsocks-x64.tar.xz">https://zohead.com/downloads/uml-shadowsocks-x64.tar.xz</a> <br />
<a href="https://pan.baidu.com/s/1dFDta9N" target="_blank">https://pan.baidu.com/s/1dFDta9N</a></p>
<p>将上面的文件下载到 VPS 某个目录并解压缩，修改 <code>uml-linux.sh</code> 文件中的转发端口，将 <a href="https://wiki.archlinux.org/index.php/Shadowsocks" target="_blank">Shadowsocks 服务端配置文件</a>放到 rootfs 的 <code>/etc/shadowsocks-libev</code> 目录，之后以 root 身份运行 <code>uml-linux.sh</code> 脚本即可。</p>
<blockquote>
<p><strong>提示</strong></p>
<p>如果需要让 ShadowsocksR 服务器使用 TCP 等方式进行域名解析，可以修改 <code>/etc/default/pdnsd</code> 文件将其中的 <code>START_DAEMON</code> 改为 <code>yes</code> 来启用 <code>pdnsd</code> 本地 DNS 服务器。</p>
</blockquote>
<h3 id="uml-ssr-download">UML ShadowsocksR 系统</h3>
<p>考虑到某些网友的需要，我还生成了 32 和 64 位的 UML ShadowsocksR 系统，都是使用的最新支持混淆的 Python 版本 ShadowsocksR 服务器，有需要的朋友可以下载测试（百度网盘为备用下载地址）。</p>
<p>32 位的 UML ShadowsocksR 系统：</p>
<p><a href="https://zohead.com/downloads/uml-shadowsocksr-i686.tar.xz">https://zohead.com/downloads/uml-shadowsocksr-i686.tar.xz</a> <br />
<a href="https://pan.baidu.com/s/1eSDrWye">https://pan.baidu.com/s/1eSDrWye</a></p>
<p>64 位的 UML ShadowsocksR 系统：</p>
<p><a href="https://zohead.com/downloads/uml-shadowsocksr-x64.tar.xz">https://zohead.com/downloads/uml-shadowsocksr-x64.tar.xz</a> <br />
<a href="https://pan.baidu.com/s/1o7F3qpc">https://pan.baidu.com/s/1o7F3qpc</a></p>
<p>ShadowsocksR 系统的 rootfs 只包含了基础 Python 环境和 ShadowsocksR 服务器程序，同样下载并解压缩，由于包含了 Python 环境，该 rootfs 解压缩之后接近 30 MB。</p>
<p>使用前可根据需要修改 <code>uml-linux.sh</code> 文件中的转发端口，将 <a href="https://github.com/breakwa11/shadowsocks-rss/wiki/config.json" target="_blank">ShadowsocksR 服务器 JSON 格式配置文件</a>放到 rootfs 的 <code>/etc/shadowsocks</code> 目录，之后以 root 身份运行 <code>uml-linux.sh</code> 脚本即可（启用 <code>pdnsd</code> 本地 DNS 服务器的方法和上面的相同）。</p>
<h2 id="uml-bbr-compare">UML 系统 BBR 效果</h2>
<p>最后对比一下我的 AlphaRacks OpenVZ VPS 上启用 BBR 前后的对比速度，这里我就直接观看同一个 1080p 的 YouTube 视频来对比统计信息了。</p>
<p>在我的江苏移动宽带环境下，启用 BBR 之前 YouTube 视频连接速度一般只能跑到 1Mbps 左右：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442863/openvz-ss-speed.png" alt="OpenVZ 未启用 BBR 的 YouTube 速度" title="OpenVZ 未启用 BBR 的 YouTube 速度"></p>
<p>而在使用 UML Shadowsocks 系统开启 BBR 之后能跑到 2Mbps，不过有的时候速度还是不太稳定：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442867/openvz-ss-speed-bbr.png" alt="OpenVZ 启用 UML BBR 的 YouTube 速度" title="OpenVZ 启用 UML BBR 的 YouTube 速度"></p>
<p>UML 目前也是主要用于调试验证 Linux 内核功能的，总之 OpenVZ 使用 UML 开启 BBR 的实际使用效果还是需要长期观察测试的，本文也主要关注 UML 系统下使用 Shadowsocks 的问题，并没有考虑通用的情况，如果文章中有任何问题还请提出指正，祝大家玩的开心。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/openvz-uml-bbr/feed/</wfw:commentRss>
		<slash:comments>40</slash:comments>
		</item>
		<item>
		<title>Linux下USB 3.0移动硬盘读写错误问题分析</title>
		<link>https://zohead.com/archives/linux-usbhdd-err/</link>
		<comments>https://zohead.com/archives/linux-usbhdd-err/#comments</comments>
		<pubDate>Tue, 08 Mar 2016 19:09:23 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[kernel]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[barrier]]></category>
		<category><![CDATA[Chromebook]]></category>
		<category><![CDATA[ext4]]></category>
		<category><![CDATA[FUA]]></category>
		<category><![CDATA[JMicron]]></category>
		<category><![CDATA[journal]]></category>
		<category><![CDATA[SCSI]]></category>
		<category><![CDATA[USB]]></category>
		<category><![CDATA[移动硬盘]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1188</guid>
		<description><![CDATA[为了解决 Chromebook 上自带 SSD 空间不足的问题，之前我在淘宝上购入了一个绿帆 F200 USB 3.0 移动硬盘盒，该硬盘盒使用的是 JMicron JMS567 这款使用还比较广泛的 SATA 6.0Gbps to USB 3.0 桥接芯片，准备配上 N 年前的神船笔记本淘汰下来的 2.5 寸硬盘给 Chromebook 使用，这样我就可以在 Chromebook 上安装的 Crouton Ubuntu 系统里无碍的使用各种编译开发环境了。 移动硬盘问题说明 首先我在宏碁 W700 Windows 10 平板上接上此移动硬盘建了一个 NTFS 分区并做各种读写拷贝大文件之类的 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>为了解决 Chromebook 上自带 SSD 空间不足的问题，之前我在淘宝上购入了一个绿帆 F200 USB 3.0 移动硬盘盒，该硬盘盒使用的是 JMicron JMS567 这款使用还比较广泛的 SATA 6.0Gbps to USB 3.0 桥接芯片，准备配上 N 年前的神船笔记本淘汰下来的 2.5 寸硬盘给 Chromebook 使用，这样我就可以在 Chromebook 上安装的 Crouton Ubuntu 系统里无碍的使用各种编译开发环境了。</p>
<h2 id="移动硬盘问题说明">移动硬盘问题说明</h2>
<p>首先我在宏碁 W700 Windows 10 平板上接上此移动硬盘建了一个 NTFS 分区并做各种读写拷贝大文件之类的测试没有任何问题；但是我把移动硬盘换到 Chromebook 上之后发现移动硬盘上新建的 ext4 分区读写会报错，无法做任何拷贝文件操作，先看看 Linux kernel 看到的 USB 3.0 移动硬盘的信息：</p>
<pre class="brush: bash; title: ; notranslate">
[ 9934.612465] usb 2-1: new SuperSpeed USB device number 2 using xhci_hcd
[ 9934.624741] usb 2-1: New USB device found, idVendor=152d, idProduct=0567
[ 9934.624768] usb 2-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 9934.624787] usb 2-1: Product: lvfan USB3.0 storage
[ 9934.624802] usb 2-1: Manufacturer: JMicron
[ 9934.624817] usb 2-1: SerialNumber: 3224043AA81395
[ 9934.626473] scsi2 : usb-storage 2-1:1.0
[ 9935.627536] scsi 2:0:0:0: Direct-Access     lvfan                     0117 PQ: 0 ANSI: 6
[ 9935.629174] sd 2:0:0:0: [sdb] Spinning up disk...
[ 9936.629940] ...ready
[ 9938.634171] sd 2:0:0:0: [sdb] 312581808 512-byte logical blocks: (160 GB/149 GiB)
[ 9938.634552] sd 2:0:0:0: [sdb] Write Protect is off
[ 9938.634580] sd 2:0:0:0: [sdb] Mode Sense: 47 00 10 08
[ 9938.634955] sd 2:0:0:0: [sdb] Write cache: enabled, read cache: enabled, supports DPO and FUA
[ 9938.659685]  sdb: sdb1 sdb2
[ 9938.661548] sd 2:0:0:0: [sdb] Attached SCSI disk
</pre>
<p>这个是拷贝文件出错时候的内核日志：</p>
<pre class="brush: bash; title: ; notranslate">
[ 9910.412002] EXT4-fs (sdb2): mounted filesystem with ordered data mode. Opts: (null)
[ 9918.500515] sd 2:0:0:0: [sdb] Invalid command failure
[ 9918.500530] sd 2:0:0:0: [sdb]  
[ 9918.500537] Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE
[ 9918.500547] sd 2:0:0:0: [sdb]  
[ 9918.500554] Sense Key : Illegal Request [current] 
[ 9918.500570] sd 2:0:0:0: [sdb]  
[ 9918.500577] Add. Sense: Invalid field in cdb
[ 9918.500589] sd 2:0:0:0: [sdb] CDB: 
[ 9918.500596] Write(10): 2a 08 04 58 18 30 00 00 08 00
[ 9918.500641] end_request: critical target error, dev sdb, sector 72882224
[ 9918.500654] end_request: critical target error, dev sdb, sector 72882224
[ 9918.500695] Aborting journal on device sdb2-8.
[ 9922.109415] EXT4-fs error (device sdb2): ext4_journal_start_sb:349: Detected aborted journal
[ 9922.109439] EXT4-fs (sdb2): Remounting filesystem read-only
</pre>
<p>看起来是报某个扇区写操作出错，那么我先在 Chromebook 上用 dd 命令直接写对应报错的扇区：</p>
<pre class="brush: bash; title: ; notranslate">
chronos@localhost ~ $ sudo dd if=/dev/zero of=/dev/sdb bs=512 count=10 seek=72882224
10+0 records in
10+0 records out
5120 bytes (5.1 kB) copied, 0.0185829 s, 276 kB/s
</pre>
<p>可以看到写之前报错的扇区其实是没有问题的，而且我使用的 SATA 笔记本硬盘之前经过 mhdd 扫描测试没有发现坏道之类的。</p>
<p>无奈我删掉这个 ext4 分区，重新创建了 ext3 分区并格式化拷贝文件但还是存在类似的错误，最后更换为 ext2 或者 FAT32 文件系统才能正常写入文件，这个就非常奇怪了，接着我把移动硬盘换到 Remix OS PC 版 4.0.9 kernel 下挂载 ext4 读写还是有一样的问题，这样我们就必须要具体分析上面的报错信息了。</p>
<h2 id="读写错误问题分析">读写错误问题分析</h2>
<p>从上面的 kernel 日志中可以看到报错的 SCSI CDB 是：<code>2a 08 04 58 18 30 00 00 08 00</code>，而 0x2A 是 SCSI WRITE 10 命令，参考 SCSI 协议可以知道其命令格式为：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737442940/scsi-rw-cmd.jpg" alt="SCSI WRITE 10命令" title="SCSI WRITE 10命令"></p>
<p>我们可以从上图知道 <code>04 58 18 30</code> 是写操作的实际扇区地址（也就是上面报错的 72882224 扇区），<code>08</code> 是写命令的标志位表示这个写命令启用了 <code>FUA</code>。</p>
<p>这里我对 <code>FUA</code>（Force Unit Access）做个简单的介绍：</p>
<ul>
<li>
<p>由于在 Linux 操作系统中从上层到下层多个地方都存在着缓存：page cache（vfs cache）-&gt; HBA/RAID卡自带的 cache -&gt; 磁盘自身的缓存，这样文件系统在写日志等关键的操作时就需要保证写入的数据被真实写到磁盘等物理介质中，而不是存留在各层 cache 里，这样可以防止系统掉电等情况导致数据不一致。</p>
</li>
<li>
<p>Linux kernel 中比较新的文件系统像 ext4、xfs、reiserfs 等则引入了 barrier 特性，遇到带 barrier 标志的写请求（或者 fsync 刷新请求）的时候必须保证之前的所有请求都已经写入到物理介质才能继续。老的 SCSI 或 SAS 磁盘支持通过 <code>SYNCHRONIZE CACHE</code> 命令刷新缓存数据，只不过这样会有额外的影响；新的 kernel 中对于支持 <code>FUA</code> 的设备则是通过发送带 <code>FUA</code> 标志的写请求来实现。</p>
</li>
<li>
<p>SCSI CDB 中的 <code>FUA</code> 标志位如果被置上则表示该请求必须通过访问物理介质实现，如果是带 <code>FUA</code> 的写命令就表示写到物理介质命令才算完成，带 <code>FUA</code> 的读命令则表示直接从物理介质读取绕过缓存。</p>
</li>
</ul>
<p>这样分析下来基本就可以推断出原因了：</p>
<p>由于 ext4、ext3 等文件系统在 kernel 中是默认启用和日志（journal）和 barrier 的，拷贝文件时需要更新文件系统日志，从最上面 kernel 报上来的移动硬盘信息显示是支持 <code>FUA</code> 的，如此日志的写入就是通过发送带 <code>FUA</code> 的写命令实现，然而我的 USB 3.0 移动硬盘响应带 <code>FUA</code> 标志的写命令出现问题导致 ext4、ext3 文件系统变为只读状态无法写入；而 ext2 和 FAT32 文件系统则是完全不带日志的，这样拷贝文件反而就没有问题。</p>
<blockquote>
<p><strong>提示</strong></p>
<p>有关我的移动硬盘使用的 JMicron JMS567 桥接芯片的详细信息请参考 <a href="http://www.jmicron.com/PDF/brief/jms567.pdf">JMicron 技术文档</a>。</p>
</blockquote>
<h2 id="解决方案">解决方案</h2>
<p>如果要顺利使用 ext4 分区，解决方案也有几种，一般的用户建议参考第二种解决方案，下面简单说明下：</p>
<h3 id="禁用-ext4-文件系统日志">禁用 ext4 文件系统日志</h3>
<p>这是比较简单粗暴的解决方案，ext4 文件系统相对 ext3 其中一个改进就是可以手工关闭日志，先卸载已挂载的 ext4 文件系统，然后运行下面的命令：</p>
<pre class="brush: bash; title: ; notranslate">
sudo tune2fs -O ^has_journal /dev/sdb2
</pre>
<p>这样就可以直接关闭 ext4 文件系统默认启用的日志功能，日志被禁用之后基本就类似 ext2 不需要专门发送带 <code>FUA</code> 的写请求了；当然这里我不建议这样做，因为日志功能对于移动硬盘这种热插拔频繁的设备还是相当有用的，可以减少文件出错或丢失的可能。</p>
<h3 id="关闭-ext4-文件系统-barrier">关闭 ext4 文件系统 barrier</h3>
<p>对于不愿意修改或者编译 Linux kernel 的用户这是比较好的解决方案咯，只是关闭 ext4 文件系统 barrier 特性可以保留最有用的日志功能，虽然相比默认启用 barrier 的情况仍然会有一点导致数据不一致的可能，但好处是修改之后就算把移动硬盘接到其它 Linux 机器上基本也能起作用，这样就比较划算了，具体运行下面的命令：</p>
<pre class="brush: bash; title: ; notranslate">
sudo tune2fs -o nobarrier /dev/sdb2
</pre>
<p>然后重新挂载 ext4 分区，一般的 Linux 系统都会在挂载该文件系统时自动禁用 barrier，Chromebook 上自带文件管理器的自动挂载磁盘功能实测有效。</p>
<h3 id="修改-kernel-禁用移动硬盘-fua">修改 kernel 禁用移动硬盘 FUA</h3>
<p>这种解决方案比较适合对编译 kernel 比较熟悉而且相对追求完美（^_^）的用户，不过相对的是需要依赖使用的 Linux 主机 kernel。</p>
<p>由于只禁用 ext4 文件系统的 barrier 还不能完全保证不会有什么别的地方需要用到带 <code>FUA</code> 的读写命令，如果能够修改 kernel 直接让我的这款移动硬盘设备报到系统中时就显示为不支持 <code>FUA</code>，这样就算遇到例如使用 barrier 等场合也可以通过 <code>SYNCHRONIZE CACHE</code> 命令来解决。</p>
<p>Linux kernel USB mass storage 驱动中内置了一个 unusual USB 设备列表，里面包含各种用户已经发现的有问题的 USB 设备及对应处理标志，该列表由 kernel 源代码中的 <code>drivers/usb/storage/unusual_devs.h</code> 文件负责维护。</p>
<p>我们可以先检查下 Chromebook 当前使用的 3.8.11 版本内核和 Remix OS PC 版使用的 4.0.9 版本内核源代码，却都能看到已经有用户报告的我的移动硬盘使用的 JMicron JMS567 芯片的 <code>FUA</code> 问题了：</p>
<pre class="brush: cpp; title: drivers/usb/storage/unusual_devs.h; notranslate">
UNUSUAL_DEV(  0x152d, 0x0567, 0x0114, 0x0114,
		&quot;JMicron&quot;,
		&quot;USB to ATA/ATAPI Bridge&quot;,
		USB_SC_DEVICE, USB_PR_DEVICE, NULL,
		US_FL_BROKEN_FUA ),
</pre>
<p>这个稍微有点奇怪，kernel 碰到带 <code>US_FL_BROKEN_FUA</code> 标志的 USB 设备会自动禁用 <code>FUA</code>，按说报上来的移动硬盘设备应该显示为不支持 <code>FUA</code> 的；不过稍微看下就发现上面的 <code>UNUSUAL_DEV</code> 条目限制了有问题的 USB 设备的 BCD 码固定为 <code>0x0114</code>，我们可以通过 <code>lsusb -v</code> 命令确认下移动硬盘设备的详细 USB 信息：</p>
<pre class="brush: bash; title: ; notranslate">
Bus 002 Device 004: ID 152d:0567 JMicron Technology Corp. / JMicron USA Technology Corp. 
Couldn't open device, some information will be missing
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               3.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0         9
  idVendor           0x152d JMicron Technology Corp. / JMicron USA Technology Corp.
  idProduct          0x0567 
  bcdDevice            1.17
</pre>
<p>可以看到移动硬盘的 USB 设备 BCD 码是 1.17 也就是 <code>0x0117</code>，这样 kernel 就不会认为我这款绿帆移动硬盘是有问题的 USB 设备进而做特殊处理了。</p>
<p>同样我们可以看看最新的 Linux 4.5-rc7 版本 kernel 中异常 USB 设备列表中的 JMS567 条目：</p>
<pre class="brush: cpp; title: drivers/usb/storage/unusual_devs.h; notranslate">
UNUSUAL_DEV(  0x152d, 0x0567, 0x0114, 0x0116,
		&quot;JMicron&quot;,
		&quot;USB to ATA/ATAPI Bridge&quot;,
		USB_SC_DEVICE, USB_PR_DEVICE, NULL,
		US_FL_BROKEN_FUA ),
</pre>
<p>稍微有点无奈最新的 Linux kernel 中的 BCD 码范围是从 0x0114 到 0x0116，还是不包含我这款移动硬盘。</p>
<p>解决步骤也就比较简单了，检出对应的内核源代码，修改 <code>drivers/usb/storage/unusual_devs.h</code> 文件可以把 JMS567 USB 设备的 BCD 码范围改为 0x0114 到 0x0117，也可以彻底点直接改成 0x0000 到 0x9999。</p>
<p>unusual_devs.h 文件修改完成后替换 usb-storage 模块并重新启动，顺利的话就可以看到报上来的 SCSI 磁盘设备显示为：</p>
<pre class="brush: bash; title: ; notranslate">
[ 7205.597952] sd 2:0:0:0: [sdb] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
</pre>
<blockquote>
<p><strong>提示</strong></p>
<p>由于 Chromebook 和 Remix OS 中的 usb-storage 驱动都是直接集成在 kernel 中的，无法只编译 usb-storage 模块进行简单替换，因此需要完整编译出 kernel bzImage 直接替换。</p>
</blockquote>
<p><br/>
<p>这样 ext4 文件系统的日志还有 barrier 特性就能愉快的继续使用了，祝各位玩的开心～～～。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/linux-usbhdd-err/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>修改Remix OS kernel支持Chromebook触控板</title>
		<link>https://zohead.com/archives/remixos-cb-trackpad/</link>
		<comments>https://zohead.com/archives/remixos-cb-trackpad/#comments</comments>
		<pubDate>Tue, 01 Mar 2016 18:16:13 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Chrome]]></category>
		<category><![CDATA[kernel]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[Chromebook]]></category>
		<category><![CDATA[chromeos_laptop]]></category>
		<category><![CDATA[cyapa]]></category>
		<category><![CDATA[cyapatp]]></category>
		<category><![CDATA[I2C]]></category>
		<category><![CDATA[Remix OS]]></category>
		<category><![CDATA[触控板]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1183</guid>
		<description><![CDATA[继续接着上一篇文章「修改Remix OS适配Chromebook键盘」的修改之后在我的 Dell Chromebook 11 上使用 ext4 U 盘形式的 Remix OS PC 版已经是比较顺利了，那么剩下最大的问题就是触控板问题了，要知道之前我使用 Remix OS 的时候都是必须插着一个 USB 鼠标的。 Chromebook 的触控板由于比较特殊之前很多网友安装的 Ubuntu 系统里经常也用不了，这里我以 Dell Chromebook 11 为例子对 Remix OS PC 版内核模块做一些修改可以实现 Remix OS 下基本的触控功能，对于在 Chromebook 上单独安装 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>继续接着上一篇文章「<a href="https://zohead.com/archives/remixos-cb-keyboard/">修改Remix OS适配Chromebook键盘</a>」的修改之后在我的 Dell Chromebook 11 上使用 ext4 U 盘形式的 Remix OS PC 版已经是比较顺利了，那么剩下最大的问题就是触控板问题了，要知道之前我使用 Remix OS 的时候都是必须插着一个 USB 鼠标的。</p>
<p>Chromebook 的触控板由于比较特殊之前很多网友安装的 Ubuntu 系统里经常也用不了，这里我以 Dell Chromebook 11 为例子对 Remix OS PC 版内核模块做一些修改可以实现 Remix OS 下基本的触控功能，对于在 Chromebook 上单独安装的 Linux 系统下想使用触控板的用户也能做一个参考。</p>
<p>下面的篇幅有很大一部分是对 Remix OS 不能使用 Chromebook 触控板的原因分析，不想了解技术细节的话可以直接拖到最后「<em>修改 Remix OS 系统文件</em>」部分下载并使用我修改编译出来的适合 Remix OS 的内核模块哦。</p>
<blockquote>
<p>编写这篇文章时我使用的是 Remix OS PC 版 2016-02-01 alpha 版本，对应 Android x86 5.1.1 版本，Linux kernel 版本 4.0.9； <br />
  我使用的 Chrome OS 版本是 48.0.2564.116 正式版，Linux kernel 版本 3.8.11。</p>
</blockquote>
<h2 id="触控板问题分析">触控板问题分析</h2>
<p>首先确认能正常工作的 Chrome OS 下的 Chromebook 触控板相关信息，看看 Linux 输入设备中的触控板设备：</p>
<pre class="brush: bash; title: ; notranslate">
I: Bus=0018 Vendor=0000 Product=0000 Version=0001
N: Name=&quot;Cypress APA Trackpad (cyapa)&quot;
P: Phys=i2c-9-0067/input0
S: Sysfs=/devices/pci0000:00/0000:00:15.1/i2c-9/9-0067/input/input7
U: Uniq=
H: Handlers=event7 
B: PROP=5
B: EV=b
B: KEY=e520 10000 0 0 0 0
B: ABS=660800001000003
</pre>
<p>可以看到 Dell Chromebook 11 上的触控板其实用的是 Cypress 家的 APA I2C 输入设备，Chrome OS 上对应 i2c-9 适配器，I2C 地址是 67，对应的 I2C 输入设备驱动为 <code>cyapa</code>。</p>
<blockquote>
<p><strong>提示</strong></p>
<p>其它也有不少三星、HP 之类的 Chromebook 设备用的是 I2C 触控板，但不一定是同一厂商的，有的是使用 ELAN 等 I2C 触控板； <br />
  另外具体机器上 I2C 地址是固定的，i2c-9 适配器则是 Linux kernel 自动生成的并不固定。</p>
</blockquote>
<p>接下来看看 I2C 适配器实际对应的设备位置和名称：</p>
<pre class="brush: bash; title: ; notranslate">
chronos@localhost ~ $ cd /sys/class/i2c-adapter/
chronos@localhost /sys/class/i2c-adapter $ ls -dl *
lrwxrwxrwx 1 root root 0 Mar  1 22:38 i2c-0 -&gt; ../../devices/pci0000:00/0000:00:02.0/i2c-0
lrwxrwxrwx 1 root root 0 Mar  1 22:38 i2c-1 -&gt; ../../devices/pci0000:00/0000:00:02.0/i2c-1
lrwxrwxrwx 1 root root 0 Mar  1 22:38 i2c-10 -&gt; ../../devices/pci0000:00/0000:00:15.2/i2c-10
lrwxrwxrwx 1 root root 0 Mar  1 22:38 i2c-2 -&gt; ../../devices/pci0000:00/0000:00:02.0/i2c-2
lrwxrwxrwx 1 root root 0 Mar  1 22:38 i2c-3 -&gt; ../../devices/pci0000:00/0000:00:02.0/i2c-3
lrwxrwxrwx 1 root root 0 Mar  1 22:38 i2c-4 -&gt; ../../devices/pci0000:00/0000:00:02.0/i2c-4
lrwxrwxrwx 1 root root 0 Mar  1 22:38 i2c-5 -&gt; ../../devices/pci0000:00/0000:00:02.0/i2c-5
lrwxrwxrwx 1 root root 0 Mar  1 22:38 i2c-6 -&gt; ../../devices/pci0000:00/0000:00:02.0/drm/card0/card0-eDP-1/i2c-6
lrwxrwxrwx 1 root root 0 Mar  1 22:38 i2c-7 -&gt; ../../devices/pci0000:00/0000:00:02.0/drm/card0/card0-DP-1/i2c-7
lrwxrwxrwx 1 root root 0 Mar  1 22:38 i2c-8 -&gt; ../../devices/pci0000:00/0000:00:1f.3/i2c-8
lrwxrwxrwx 1 root root 0 Mar  1 22:38 i2c-9 -&gt; ../../devices/pci0000:00/0000:00:15.1/i2c-9
chronos@localhost /sys/class/i2c-adapter $ cat i2c-*/name
i915 gmbus ssc
i2c-designware-pci-1
i915 gmbus vga
i915 gmbus panel
i915 gmbus dpc
i915 gmbus dpb
i915 gmbus dpd
DPDDC-A
DPDDC-B
SMBus I801 adapter at 0400
i2c-designware-pci-0
</pre>
<p>可以看到触控板的 I2C 适配器 i2c-9 在 PCI 设备 0000:00:15.1 上，名称为：<code>i2c-designware-pci-0</code>，另外系统里还有 i2c-10 适配器在 0000:00:15.2 上，名称为：<code>i2c-designware-pci-1</code>，这两个适配器对应的 I2C 总线驱动是：<code>i2c-designware-pci</code>。</p>
<p>下面就可以在 Chromebook 上启动 Remix OS 确认其内核下 I2C 设备信息是否正确：</p>
<pre class="brush: bash; title: ; notranslate">
chronos@localhost ~ $ cd /sys/class/i2c-adapter/
root@remix_cn_x86_64:/sys/class/i2c-adapter # ls -dl *
lrwxrwxrwx root     root              2016-03-01 00:24 i2c-0 -&gt; ../../devices/pci0000:00/0000:00:02.0/i2c-0
lrwxrwxrwx root     root              2016-03-01 00:24 i2c-1 -&gt; ../../devices/pci0000:00/0000:00:02.0/i2c-1
lrwxrwxrwx root     root              2016-03-01 00:24 i2c-10 -&gt; ../../devices/pci0000:00/0000:00:1f.3/i2c-10
lrwxrwxrwx root     root              2016-03-01 00:24 i2c-2 -&gt; ../../devices/pci0000:00/0000:00:02.0/i2c-2
lrwxrwxrwx root     root              2016-03-01 00:24 i2c-3 -&gt; ../../devices/pci0000:00/0000:00:02.0/i2c-3
lrwxrwxrwx root     root              2016-03-01 00:24 i2c-4 -&gt; ../../devices/pci0000:00/0000:00:02.0/i2c-4
lrwxrwxrwx root     root              2016-03-01 00:24 i2c-5 -&gt; ../../devices/pci0000:00/0000:00:02.0/i2c-5
lrwxrwxrwx root     root              2016-03-01 00:24 i2c-6 -&gt; ../../devices/pci0000:00/0000:00:02.0/i2c-6
lrwxrwxrwx root     root              2016-03-01 00:24 i2c-7 -&gt; ../../devices/pci0000:00/0000:00:02.0/i2c-7
lrwxrwxrwx root     root              2016-03-01 00:24 i2c-8 -&gt; ../../devices/pci0000:00/0000:00:15.1/i2c-8
lrwxrwxrwx root     root              2016-03-01 00:24 i2c-9 -&gt; ../../devices/pci0000:00/0000:00:15.2/i2c-9
root@remix_cn_x86_64:/sys/class/i2c-adapter # cat i2c-*/name
i915 gmbus ssc
i915 gmbus vga
SMBus I801 adapter at 0400
i915 gmbus panel
i915 gmbus dpc
i915 gmbus dpb
i915 gmbus dpd
DPDDC-A
DPDDC-B
i2c-designware-pci
i2c-designware-pci
</pre>
<p>这样可以发现 Remix OS 内核下两个 I2C 适配器为 i2c-8 和 i2c-9，虽然 i2c-8 是触控板设备但是使用起来应该是没有影响的，I2C 总线驱动也是正确的，但适配器名称都是一样的 <code>i2c-designware-pci</code>。</p>
<p>同时进一步确认之后发现 Remix OS 内核下虽然没有 Chrome OS 里直接使用的 <code>cyapa</code> 驱动，但其默认集成了新的 Linux kernel 里自带的 <code>cyapatp</code> 驱动。<code>cyapa</code> 是 Google 为 Chromebook 内核专门修改的 Cypress Gen3 系列触控板驱动，而查看源代码之后发现 <code>cyapatp</code> 驱动不但支持 Cypress Gen3 系列触控板，也支持新的 Gen5 系列触控板，这样按说 <code>cyapatp</code> 直接就是支持我的 Chromebook 触控板设备的。</p>
<p>比较郁闷的是我直接在 Remix OS 终端里运行 <code>modprobe cyapatp</code> 命令虽然能成功加载驱动，但系统中并没有注册任何新的输入设备。</p>
<h2 id="chromebook-kernel-研究">Chromebook kernel 研究</h2>
<p>没办法就只能先检出 Chrome OS 当前使用的 3.8.11 版本内核源代码研究下了，检出方法可以参考我之前写的「<a href="https://zohead.com/archives/koding-chromebook/">在Koding云平台上编译Chromebook kernel</a>」这篇文章，Chrome OS kernel 分支可以在这里看到：</p>
<p><a href="https://chromium.googlesource.com/chromiumos/third_party/kernel/">https://chromium.googlesource.com/chromiumos/third_party/kernel/</a></p>
<p>稍微研究后发现原来 Chrome OS kernel 里增加了一个 <code>chromeos_laptop</code> 平台相关的驱动，Chrome OS 系统启动时会加载这个驱动（实际上是直接编译到 Chrome OS kernel 中的），<code>chromeos_laptop</code> 中会自动尝试查找并实例化 Chromebook 上的 I2C/SMBus 设备，看看其中的部分代码：</p>
<pre class="brush: cpp; title: chromeos_laptop.c; notranslate">
#define CYAPA_TP_I2C_ADDR	0x67

const char *i2c_adapter_names[] = {
	&quot;SMBus I801 adapter&quot;,
	&quot;i915 gmbus vga&quot;,
	&quot;i915 gmbus panel&quot;,
	&quot;i2c-designware-pci-0&quot;,
	&quot;i2c-designware-pci-1&quot;,
};

/* Keep this enum consistent with i2c_adapter_names */
enum i2c_adapter_type {
	I2C_ADAPTER_SMBUS = 0,
	I2C_ADAPTER_VGADDC,
	I2C_ADAPTER_PANEL,
	I2C_ADAPTER_I2C0,
	I2C_ADAPTER_I2C1,
};

static struct i2c_board_info cyapa_device = {
	I2C_BOARD_INFO(&quot;cyapa&quot;, CYAPA_TP_I2C_ADDR),
	.flags		= I2C_CLIENT_WAKE,
};

static struct chromeos_laptop wolf = {
	.i2c_peripherals = {
		/* Touchpad. */
		{ .add = setup_cyapa_tp, I2C_ADAPTER_I2C0 },
		/* Elan Touchpad option. */
		{ .add = setup_elantech_tp, I2C_ADAPTER_I2C0 },
	},
};

static struct i2c_client *add_i2c_device(const char *name,
						enum i2c_adapter_type type,
						struct i2c_board_info *info)
{
	const unsigned short addr_list[] = { info-&gt;addr, I2C_CLIENT_END };
	return __add_probed_i2c_device(name,
				       find_i2c_adapter_num(type),
				       info,
				       addr_list);
}

static int setup_cyapa_tp(enum i2c_adapter_type type)
{
	if (tp)
		return 0;

	/* add cyapa touchpad */
	tp = add_i2c_device(&quot;trackpad&quot;, type, &amp;cyapa_device);
	return (!tp) ? -EAGAIN : 0;
}
</pre>
<p>稍微分析下可以看到 <code>chromeos_laptop</code> 驱动是根据名称找到当前系统中的 I2C 适配器的，找到之后最后根据 I2C 地址调用 <code>i2c_new_probed_device</code> 函数实例化 I2C 设备，而这里 <code>cyapa</code> 触控板驱动使用的 I2C 适配器名称固定为 <code>i2c-designware-pci-0</code>。</p>
<p>这样就有 Remix OS 下使用触控板的解决思路了，我们可以修改 <code>i2c-designware-pci</code> 驱动使其报上来的两个 I2C 适配器使用不同的名称，并加入 <code>chromeos_laptop</code> 驱动这样就可以正常使用新的 <code>cyapatp</code> 驱动；或者也可以不修改 <code>i2c-designware-pci</code> 而直接加入并修改 <code>chromeos_laptop</code> 中的触控板 I2C 适配器名称，为了后续 I2C 的区分我决定用第一种方法。</p>
<h2 id="remix-os-kernel-修改">Remix OS kernel 修改</h2>
<p>首先我们就要检出技德不久前开放的 Remix OS kernel 代码了，地址在这里：</p>
<p><a href="https://github.com/jide-opensource/remixos-kernel/">https://github.com/jide-opensource/remixos-kernel/</a></p>
<p>值得一提的是 Remix OS 内核基本就是使用的默认 android-x86_64_defconfig 配置，先开始修改 <code>i2c-designware-pcidrv.c</code> 代码以支持使用不同的 I2C 适配器名称：</p>
<pre class="brush: diff; title: ; notranslate">
--- a/i2c-designware-pcidrv.c	2016-03-02 01:04:19.330843871 +0800
+++ b/i2c-designware-pcidrv.c	2016-03-01 00:18:04.502977140 +0800
@@ -48,7 +48,8 @@ enum dw_pci_ctl_id_t {
 	medfield_5,
 
 	baytrail,
-	haswell,
+	haswell_0,
+	haswell_1,
 };
 
 struct dw_scl_sda_cfg {
@@ -148,7 +149,15 @@ static struct dw_pci_controller dw_pci_c
 		.functionality = I2C_FUNC_10BIT_ADDR,
 		.scl_sda_cfg = &amp;byt_config,
 	},
-	[haswell] = {
+	[haswell_0] = {
+		.bus_num = -1,
+		.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
+		.tx_fifo_depth = 32,
+		.rx_fifo_depth = 32,
+		.functionality = I2C_FUNC_10BIT_ADDR,
+		.scl_sda_cfg = &amp;hsw_config,
+	},
+	[haswell_1] = {
 		.bus_num = -1,
 		.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
 		.tx_fifo_depth = 32,
@@ -258,7 +267,8 @@ static int i2c_dw_pci_probe(struct pci_d
 	adap-&gt;dev.parent = &amp;pdev-&gt;dev;
 	adap-&gt;nr = controller-&gt;bus_num;
 
-	snprintf(adap-&gt;name, sizeof(adap-&gt;name), &quot;i2c-designware-pci&quot;);
+	snprintf(adap-&gt;name, sizeof(adap-&gt;name), &quot;i2c-designware-pci-%ld&quot;,
+		(adap-&gt;nr &lt; 0) ? id-&gt;driver_data - haswell_0 : adap-&gt;nr);
 
 	r = devm_request_irq(&amp;pdev-&gt;dev, pdev-&gt;irq, i2c_dw_isr, IRQF_SHARED,
 			adap-&gt;name, dev);
@@ -314,8 +324,8 @@ static const struct pci_device_id i2_des
 	{ PCI_VDEVICE(INTEL, 0x0F46), baytrail },
 	{ PCI_VDEVICE(INTEL, 0x0F47), baytrail },
 	/* Haswell */
-	{ PCI_VDEVICE(INTEL, 0x9c61), haswell },
-	{ PCI_VDEVICE(INTEL, 0x9c62), haswell },
+	{ PCI_VDEVICE(INTEL, 0x9c61), haswell_0 },
+	{ PCI_VDEVICE(INTEL, 0x9c62), haswell_1 },
 	/* Braswell / Cherrytrail */
 	{ PCI_VDEVICE(INTEL, 0x22C1), baytrail },
 	{ PCI_VDEVICE(INTEL, 0x22C2), baytrail },
</pre>
<p>然后将 Chrome OS 3.8.11 版本 kernel 中的 <code>chromeos_laptop</code> 平台相关驱动加到 Remix OS 4.0.9 版本 kernel 中，最后开始编译内核 bzImage 和对应的内核模块文件，实际上编译 bzImage 只是为了生成需要的 Module.symvers 符号文件，真正要使用的是新编译生成的 <code>i2c-designware-pci.ko</code> 和 <code>chromeos_laptop.ko</code> 文件。</p>
<blockquote>
<p><strong>提示</strong></p>
<p>我是在 Chromebook Crouton 64 位 Ubuntu 14.04 环境中检出并修改 Remix OS kernel 进行编译的，实际 kernel 修改、加入新驱动修改 Makefile 和 Kconfig 等还有编译操作步骤什么的这里就不说明了，编译中间可能会有无线驱动报错，稍微修改下内核配置就可以通过了。</p>
</blockquote>
<h2 id="修改-remix-os-系统文件">修改 Remix OS 系统文件</h2>
<p>我编译好的两个内核模块文件下载地址（只适用于 Remix OS PC 版 alpha 4.0.9 内核）：</p>
<p><a href="https://zohead.com/downloads/cbtp-remixos-20160201alpha.zip">https://zohead.com/downloads/cbtp-remixos-20160201alpha.zip</a></p>
<p>首先可以在 Chromebook 上将新的内核模块文件放到 Remix OS U 盘中，如果你使用的是按照我之前的「<a href="https://zohead.com/archives/cb-ext4-remixos/">Chromebook使用ext4 U盘运行Remix OS</a>」文章介绍的方法生成的 ext4 格式 U 盘，那么修改替换 Remix OS 系统文件还是非常简单的。</p>
<pre class="brush: bash; title: ; notranslate">
(trusty)zzm@localhost:~$ sudo cp i2c-designware-pci.ko /media/removable/Android-x86/android-2016-02-02/system/lib/modules/4.0.9-android-x86_64+/kernel/drivers/i2c/busses/
(trusty)zzm@localhost:~$ sudo cp chromeos_laptop.ko /media/removable/Android-x86/android-2016-02-02/system/lib/modules/4.0.9-android-x86_64+/kernel/drivers/platform/x86/
</pre>
<blockquote>
<p><strong>注意</strong></p>
<p>上面的命令例子中假定 Remix OS U 盘已经自动被 Chrome OS 挂载到 <code>/media/removable/Android-x86</code> 目录了，<code>android-2016-02-02</code> 是 Remix OS 系统路径，实际的挂载和系统路径不完全相同请自行检查。</p>
</blockquote>
<p>下面是使用 <code>depmod</code> 命令重新生成新的模块依赖关系等文件，这样 Remix OS 系统在启动的时候就会自动尝试加载 <code>chromeos_laptop</code> 驱动，接着自动加载 <code>cyapatp</code> 驱动成功之后就可以直接 Chromebook 触控板了。</p>
<pre class="brush: bash; title: ; notranslate">
sudo depmod -A -b /media/removable/Android-x86/android-2016-02-02/system 4.0.9-android-x86_64+
</pre>
<p>最后说下我简单测试之后 Remix OS 中 Chromebook 触控板的使用效果：</p>
<ul>
<li>基本的移动、点击等操作可以正常工作；</li>
<li>看起来也是支持简单的多点触控的；</li>
<li>Remix OS 桌面图标的双击功能不起作用；</li>
<li>Chrome OS 下特色的两指上下滚动功能也可以正常使用（不过由于 Android 的自然滚动方式所以方向和 Chrome OS 下是相反的）；</li>
<li>某些 App 下面支持三指同时长按然后放开之后出现右键菜单；</li>
<li>Chrome OS 下特色的两指同时点击呼出右键菜单功能无效。</li>
</ul>
<p>总之虽然触控版的使用不像 Chrome OS 下那样完美但基本还是能满足需求的，本文中有任何错误或疑问欢迎提出指正哦，祝玩的开心～～～。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/remixos-cb-trackpad/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>在Koding云平台上编译Chromebook kernel</title>
		<link>https://zohead.com/archives/koding-chromebook/</link>
		<comments>https://zohead.com/archives/koding-chromebook/#comments</comments>
		<pubDate>Sat, 21 Mar 2015 16:39:08 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[kernel]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[ARM]]></category>
		<category><![CDATA[binfmt_misc]]></category>
		<category><![CDATA[Chrome OS]]></category>
		<category><![CDATA[Chromebook]]></category>
		<category><![CDATA[cifs]]></category>
		<category><![CDATA[Koding]]></category>
		<category><![CDATA[Secure Shell]]></category>
		<category><![CDATA[内核模块]]></category>

		<guid isPermaLink="false">http://zohead.com/?p=880</guid>
		<description><![CDATA[最近从淘宝收了一台三星的 Series 3 ARM Chromebook，平时当作上网本用起来还是可以的，可以运行一些 Chrome packaged 原生 App，Chrome OS 的运行速度也还凑合，主要电池续航比一般的笔记本要给力多了，这篇博文的编辑及实际 kernel 编译操作我都是在 Chromebook 上完成的。 由于 Chromebook 底层使用的还是 Linux 内核，装了 Crouton 插件之后可以在 Chromebook 里以 chroot 的方式跑另外的 Ubuntu 系统，只不过 Chromebook 自带的内核模块有些缺失，比如我需要的 binfmt_misc [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>最近从淘宝收了一台三星的 Series 3 ARM Chromebook，平时当作上网本用起来还是可以的，可以运行一些 Chrome packaged 原生 App，Chrome OS 的运行速度也还凑合，主要电池续航比一般的笔记本要给力多了，这篇博文的编辑及实际 kernel 编译操作我都是在 Chromebook 上完成的。</p>
<p>由于 Chromebook 底层使用的还是 Linux 内核，装了 Crouton 插件之后可以在 Chromebook 里以 chroot 的方式跑另外的 Ubuntu 系统，只不过 Chromebook 自带的内核模块有些缺失，比如我需要的 binfmt_misc 和 cifs 模块都是没有的，特别是很有用的 cifs 模块用于挂载 Windows 机器上的共享目录。因此想自己编译 Chromebook 的 kernel 加入需要的内核模块。</p>
<p>三星 Series 3 ARM Chromebook 自带的 16GB SSD 毕竟有点小，ARM 系统里装一堆开发环境还是嫌麻烦，还有比较重要的一点是国内的网络环境 git clone 出 Google Source 上的 kernel 源代码速度实在是太慢了，于是就想着用之前注册的 Koding 云端开发平台上跑交叉编译器来编译 Chromebook 的 kernel。</p>
<p><a href="http://www.koding.com/R/zohead" target="_blank">Koding</a> 云端开发平台默认自带了 Node.js、Python、Ruby、Go 等开发环境支持，Koding 默认给了一个很强大的 Web IDE 编辑工具，可以在 Web 上直接登录 Shell 操作，而且 Koding 提供的 Linux 虚拟环境默认就安装了 gcc、Makefile 等工具，其免费套餐提供的 1GB RAM、3GB 硬盘空间用来编译 kernel 也勉强够用了。</p>
<p>先在 Chromebook 里按 Ctrl +Alt + T 键切换到 crosh Shell 里确认当前 kernel 版本：</p>
<pre class="brush: bash; title: ; notranslate">
chronos@localhost / $ cat /proc/version
Linux version 3.8.11 (chrome-bot@build44-m2) (gcc version 4.9.x-google 20140827 (prerelease) (4.9.1_cos_gg_2f9796c_4.9.1-r82) ) #1 SMP Sat Mar 7 06:47:35 PST 2015
</pre>
<p>可以看到现在用的是最新 Linux 3.8.11 版本，从 Google 官方提供的 kernel 版本库里确认需要使用的 kernel 分支：</p>
<p><a href="https://chromium.googlesource.com/chromiumos/third_party/kernel/" target="_blank">https://chromium.googlesource.com/chromiumos/third_party/kernel/</a></p>
<p>我需要的 3.8.11 版本就可以直接使用 chromeos-3.8 分支，现在可以 SSH 远程登录到 Koding Linux 系统上，幸运的是我们还可以在 Chromebook 安装 <a href="https://chrome.google.com/webstore/detail/secure-shell/pnhechapfaindjhompbnflcldabbghjo" target="_blank">Secure Shell</a> 这一牛叉 App 直接进行 SSH 登录，Secure Shell 支持 Koding 的 SSH 证书（需要自己登录到 Koding 的 Web Shell 里生成哈）形式，不过选择证书时需要注意把公钥和私钥文件同时选上。</p>
<p>Koding 云端开发平台默认使用的是 Ubuntu 14.04 LTS 64 位系统，这样也比较好，ARM 交叉编译器安装起来也很方便了，先 clone 出 kernel 源代码：</p>
<pre class="brush: bash; title: ; notranslate">
git clone https://chromium.googlesource.com/chromiumos/third_party/kernel -b chromeos-3.8
</pre>
<p>然后运行 <strong>sudo apt-get install gcc-arm-linux-gnueabi</strong> 安装交叉编译器，Ubuntu 提供的 ARM gcc 编译器是 4.7.2 版本。</p>
<p>接着需要把 Chromebook 当前的 kernel 配置导出来，在 Chromebook 上切换到 Shell，运行 <strong>sudo modprobe configs</strong> 命令，然后拷贝 <strong>/proc/config.gz</strong> 文件并解压缩，通过 scp 命令将解压缩出来的 <strong>config</strong> 文件拷贝到 Koding Linux 系统中。</p>
<p>在 Koding Linux 系统中进入刚才 clone 出来的 Linux kernel 源代码目录，将 Chromebook 上拷贝过来的 <strong>config</strong> 文件重命名为 <strong>.config</strong>，然后运行下面的命令进行 kernel 配置：</p>
<pre class="brush: bash; title: ; notranslate">
cd ~/kernel
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- menuconfig
</pre>
<p>我们可以在 Network Filesystem 中启用 cifs 模块，同时可以启用 binfmt_misc 模块，保存 kernel 配置，运行下面的命令直接开始内核编译：</p>
<pre class="brush: bash; title: ; notranslate">
cd ~/kernel
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
</pre>
<p>如果你不想编译完整的 Chromebook kernel，也可以使用这两个命令只编译需要的 binfmt_misc 和 cifs 模块，这样可以节省时间：</p>
<pre class="brush: bash; title: ; notranslate">
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- M=fs/cifs
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- fs/binfmt_misc.ko
</pre>
<p>需要注意的是这样编译出来的 cifs.ko 模块文件还是依赖 md4.ko 文件（会自动编译出来）的。</p>
<p>下面就是通过 scp 把编译出来的 cifs.ko、md4.ko、binfmt_misc.ko 文件拷贝到 Chromebook 系统中，我们可以将这 3 个文件放到 Linux 内核模块目录中（不过这一步需要在 Chromebook 的开发模式中禁用 Chrome OS 的 rootfs 分区锁定功能）：</p>
<pre class="brush: bash; title: ; notranslate">
mkdir /lib/modules/3.8.11/kernel/fs/cifs
mv cifs.ko /lib/modules/3.8.11/kernel/fs/cifs
mv binfmt_misc.ko /lib/modules/3.8.11/kernel/fs
mv md4.ko /lib/modules/3.8.11/kernel/crypto
</pre>
<p>然后可以运行 sudo depmod -a 命令让系统自动生成内核模块依赖关系。</p>
<p>需要注意的是如果你把新编译出来的 ko 模块文件放在不是 rootfs 分区里（未禁用 Chrome OS 的 rootfs 分区保护等情况），这样 insmod binfmt_misc.ko 等命令加载模块会报错失败：</p>
<pre class="brush: plain; title: ; notranslate">
Chromium OS LSM: init_module denied module=&quot;/home/chronos/user/Downloads/binfmt_misc.ko&quot; pid=12499 cmdline=&quot;insmod binfmt_misc.ko&quot;
</pre>
<p>这是由于 Chrome OS 默认做了限制即不允许加载放在 rootfs 分区以外的文件系统上的内核模块文件，我们也可以运行下面的命令解除这个限制：</p>
<pre class="brush: bash; title: ; notranslate">
echo 0 &gt; /proc/sys/kernel/chromiumos/module_locking
</pre>
<p>当然如果你将模块拷贝到上面提到的标准内核模块目录中的话就不会有这个问题了。</p>
<p>经过测试这样的方法编译的模块使用起来还是没有问题的，当然还是有一个不爽之处就是如果 Chrome OS 升级之后内核版本也升级了，但这些模块也需要重新编译了，文中有任何问题还是欢迎提出指正哦，祝玩的开心 ^_^</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/koding-chromebook/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>NFS和64位inode的问题</title>
		<link>https://zohead.com/archives/nfs-64bitinode/</link>
		<comments>https://zohead.com/archives/nfs-64bitinode/#comments</comments>
		<pubDate>Thu, 24 Jul 2014 15:41:16 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[存储]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[64位]]></category>
		<category><![CDATA[enable_ino64]]></category>
		<category><![CDATA[inode]]></category>
		<category><![CDATA[inode64]]></category>
		<category><![CDATA[kernel]]></category>
		<category><![CDATA[NFS]]></category>
		<category><![CDATA[XFS]]></category>
		<category><![CDATA[文件系统]]></category>

		<guid isPermaLink="false">http://zohead.com/?p=733</guid>
		<description><![CDATA[本文同步自（最佳显示效果请点击）：https://zohead.com/archives/nfs-64bitinode/ 最近在 XFS 文件系统上使用 NFS 时发现一些比较老的 Linux 客户端在挂载时会提示 stale file handle 错误，这似乎是服务器端的 NFS 共享文件夹信息不正确了，比较奇怪为什么新的 Linux 系统又是可以挂载使用的，准备一探究竟。 首先登录到服务器端（也是 Linux 系统，RHEL6 x86_64 服务器），查看 NFS 共享文件夹的状态（NFS 共享路径为 /nfs/share2）： 上面的 ls 命令特别增加了 -i 参数用于显示文件的 i [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>本文同步自（最佳显示效果请点击）：<a href="https://zohead.com/archives/nfs-64bitinode/" target="_blank">https://zohead.com/archives/nfs-64bitinode/</a></p>
<p>最近在 XFS 文件系统上使用 NFS 时发现一些比较老的 Linux 客户端在挂载时会提示 stale file handle 错误，这似乎是服务器端的 NFS 共享文件夹信息不正确了，比较奇怪为什么新的 Linux 系统又是可以挂载使用的，准备一探究竟。</p>
<p>首先登录到服务器端（也是 Linux 系统，RHEL6 x86_64 服务器），查看 NFS 共享文件夹的状态（NFS 共享路径为 /nfs/share2）：</p>
<p><pre class="brush: bash; highlight: [3,5,7]; title: ; notranslate">
/nfs # ls -dil *
    259 drwxrwxrwx    2 root     root             6 Jul 15 15:31 share1
4294967616 drwxrwxrwx    3 root     root            24 Jul 15 16:09 share2
/nfs # ls -dil share2/*
8589934912 drwxrwxrwx    2 root     root            24 Jul 15 16:09 share2/ppp
/nfs # ls -dil share2/ppp/*
8589934913 -rwxrwxrwx    1 root     root            15 Jul 15 16:15 share2/ppp/ooo
</pre>
</p>
<p>上面的 ls 命令特别增加了 -i 参数用于显示文件的 inode 值，这时就发现 /nfs/share2、/nfs/share2/ppp 这两个文件夹和 /nfs/share2/ppp/ooo 文件的 inode 值非常大，已经超过了一般的 32 位 inode 值限制，分别为：4294967616、8589934912、8589934913，而 /nfs/share1 文件夹的 inode 值则是 259。</p>
<p>查看运行 mount 命令发现 XFS 文件系统在挂载时使用了 inode64 参数指定使用 64 位的 inode 值来存储文件，使用 64 位 inode 主要用于解决容量非常大的文件系统在有可用空间但在使用 32 位 inode 值时可能还是无法再写入文件的问题的。然后就在之前不能挂载的 Linux 客户端上重新挂载 /nfs/share1 这个文件夹，结果却可以挂载了，看来很有可能就是 64 位 inode 造成不能挂载 /nfs/share2 共享文件夹的问题。</p>
<p>后来经过查找 kernel 中 NFS 文件系统的说明，Linux kernel 在 2.6.23 版本中可以直接支持 64 位的 NFS 文件 inode 值了，而且是通过给 nfs 这个内核模块增加 enable_ino64 参数来设置的，这个参数还是运行时可写的，一般新的发行版中如果 kernel 版本高于 2.6.23 都会支持的。另外有些发行厂商例如 RedHat 会在更新中提供支持，例如 RHEL 4 Update 7 这个比较老的系统中也支持 NFS 64 位 inode 值了。</p>
<p>下面在几个常见的 Linux 客户端系统中测试对 NFS 64 位 inode 的支持情况了：</p>
<p><strong>1、Fedora Core 5 32位系统（内核版本：2.6.22）：</strong></p>
<p>32 位的 Fedora Core 5 系统内核版本 2.6.22，虽然不支持 NFS 64 位 inode（也不支持 enable_ino64 参数），但还是能挂载的，而且使用时全部显示为 32 位 inode 值，看看效果：</p>
<p><pre class="brush: bash; highlight: [4,6,9]; title: ; notranslate">
[root@zzmlinux root]# mount -t nfs 192.168.1.145:/nfs/share2 /mnt
[root@zzmlinux root]# cd /mnt/
[root@zzmlinux mnt]# ls -dil
321 drwxrwxrwx  3 root root 24 Jul 15  2014 .
[root@zzmlinux mnt]# ls -dil *
322 drwxrwxrwx  2 root root 24 Jul 15  2014 ppp
[root@zzmlinux mnt]# cd ppp/
[root@zzmlinux ppp]# ls -dil *
323 -rwxrwxrwx  1 root root 5 Jul 15  2014 ooo
[root@zzmlinux ppp]# echo &quot;test something&quot; &gt; ooo
[root@zzmlinux ppp]# cat ooo
test something
</pre>
</p>
<p>从上面的命令输出结果可以看出：</p>
<p>/nfs/share2 文件夹本身在服务器端的 inode 值原本为 64 位的 4294967616，但在 32 位的 NFS 客户端这被转换成了 321（其实：4294967616 = 0xFFFFFFFF + 321）。相应的 /nfs/share2/ppp 文件夹的 inode 值由 8589934912 转换为 322 了（8589934912 = 2 * 0xFFFFFFFF + 322），/nfs/share2/ppp/ooo 文件的 inode 值由 8589934913 转换为 323 了（8589934913 = 2 * 0xFFFFFFFF + 323）。</p>
<p>这样 NFS 客户端算是也能正常使用了，但不能保证文件数量太多时不会出现问题的。</p>
<p><strong>2、RHEL 6 64位系统（内核版本：2.6.32）：</strong></p>
<p><pre class="brush: bash; highlight: [3,5,7]; title: ; notranslate">
[root@localhost work]# mount -t nfs 192.168.1.145:/nfs/share2 /mnt/nfs
[root@localhost work]# ls -dil /mnt/nfs
4294967616 drwxrwxrwx 3 root root 24  7月 15 2014 /mnt/nfs
[root@localhost work]# ls -dil /mnt/nfs/ppp
8589934912 drwxrwxrwx 2 root root 24  7月 15 2014 /mnt/nfs/ppp
[root@localhost work]# ls -dil /mnt/nfs/ppp/ooo
8589934913 -rwxrwxrwx 1 root root 15  7月 15 2014 /mnt/nfs/ppp/ooo
[root@localhost work]# cat /mnt/nfs/ppp/ooo
test something
</pre>
</p>
<p>RHEL6 64 位系统上的效果就比较明显了，所有文件显示的 inode 值与服务器端的完全一致，这样使用起来就完全没有问题了。</p>
<p><strong>3、Fedora 14 32位系统（内核版本：2.6.35）：</strong></p>
<p><pre class="brush: bash; highlight: [2,5,7,9]; title: ; notranslate">
[root@fedora14 ~]# cat /sys/module/nfs/parameters/enable_ino64
Y
[root@fedora14 ~]# mount -t nfs 192.168.1.145:/nfs/share2 /mnt
[root@fedora14 ~]# ls -dil /mnt
4294967616 drwxrwxrwx 3 root root 24 Jul 15 16:09 /mnt
[root@fedora14 ~]# ls -dil /mnt/ppp
8589934912 drwxrwxrwx 2 root root 24 Jul 15 16:09 /mnt/ppp
[root@fedora14 ~]# ls -dil /mnt/ppp/ooo
8589934913 -rwxrwxrwx 1 root root 15 Jul 15 16:15 /mnt/ppp/ooo
</pre>
</p>
<p>这里由于 Fedora 14 是 32 位系统，我们在挂载 NFS 之前特地查看了 enable_ino64 参数，发现默认已启用的。这样在挂载之后查看到的文件的 inode 值也是和服务器一样为 64 位的了。如果有兴趣将 enable_ino64 参数改为 N 的话，你会发现这些 inode 也被转换为 32 位的了。</p>
<p>其实我碰到的主要就是 Linux 下老的程序和新的 64 位 inode 的兼容问题，SGI 推荐用户使用 64 位的 inode，btrfs 和 ext4 现在也开始准备改为 64 位 inode 了，当然不免还有一些老的程序存在兼容性问题，这里有 SGI 的研发人员实际测试 64 位 inode 的兼容结果：</p>
<p><a href="http://blog.fmeh.org/2013/05/11/does-the-world-need-32-bit-inodes/" target="_blank">http://blog.fmeh.org/2013/05/11/does-the-world-need-32-bit-inodes/</a></p>
<p>上面看到的只使用 32 位 inode 的程序占比结果 10% 还是比较令人满意的，只支持 32 位 inode 的程序现在越来越少了。</p>
<p>本文为个人测试及分析结果，如其中存在错误还请提出指正哦，玩的开心~~~ ^_^</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/nfs-64bitinode/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>NFS读写块大小问题分析</title>
		<link>https://zohead.com/archives/nfs-rwsize/</link>
		<comments>https://zohead.com/archives/nfs-rwsize/#comments</comments>
		<pubDate>Sat, 05 Jul 2014 14:44:32 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[kernel]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[存储]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[NFS]]></category>
		<category><![CDATA[TCP]]></category>
		<category><![CDATA[UDP]]></category>
		<category><![CDATA[块大小]]></category>

		<guid isPermaLink="false">http://zohead.com/?p=723</guid>
		<description><![CDATA[本文同步自（最佳显示效果请点击）：https://zohead.com/archives/nfs-rwsize/ Linux NFS 客户端在挂载服务器的 NFS 共享时可以使用 rsize 和 wsize 参数指定 NFS 读写的块大小，但实际使用时发现并不完全凑效，下面简单分析一下。 我先在一台 RHEL6 客户端上挂载另一台 RHEL6 服务器上的 NFS 共享： 从上面可以看到不指定 rsize 和 wsize 参数时，默认的读写块大小都是 256KB（rsize=262144），而且使用的是 TCP 协议（proto=tcp）。 下面使用 UDP 协议挂载 NFS 共享： 从结果可以 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>本文同步自（最佳显示效果请点击）：<a href="https://zohead.com/archives/nfs-rwsize/" target="_blank">https://zohead.com/archives/nfs-rwsize/</a></p>
<p>Linux NFS 客户端在挂载服务器的 NFS 共享时可以使用 rsize 和 wsize 参数指定 NFS 读写的块大小，但实际使用时发现并不完全凑效，下面简单分析一下。</p>
<p>我先在一台 RHEL6 客户端上挂载另一台 RHEL6 服务器上的 NFS 共享：</p>
<p><pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# mount -t nfs 192.168.1.122:/nfs/share /mnt/nfs
[root@localhost ~]# grep /mnt/nfs /proc/mounts
192.168.1.122:/nfs/share /mnt/nfs nfs rw,relatime,vers=3,rsize=262144,wsize=262144,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=192.168.1.122,mountvers=3,mountport=892,mountproto=udp,local_lock=none,addr=192.168.1.122 0 0
</pre>
</p>
<p>从上面可以看到不指定 rsize 和 wsize 参数时，默认的读写块大小都是 256KB（rsize=262144），而且使用的是 TCP 协议（proto=tcp）。</p>
<p>下面使用 UDP 协议挂载 NFS 共享：</p>
<p><pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# mount -t nfs -o udp 192.168.1.122:/nfs/share /mnt/nfs
[root@localhost ~]# grep /mnt/nfs /proc/mounts
192.168.1.122:/nfs/share /mnt/nfs nfs rw,relatime,vers=3,rsize=32768,wsize=32768,namlen=255,hard,proto=udp,timeo=11,retrans=3,sec=sys,mountaddr=192.168.1.122,mountvers=3,mountport=892,mountproto=udp,local_lock=none,addr=192.168.1.122 0 0
</pre>
</p>
<p>从结果可以看出，使用 UDP 协议时块大小就只有 32KB 了。</p>
<p>准备在客户端这边修改 mount 参数将 NFS TCP 方式的读写块大小增加到 1MB：</p>
<p><pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# mount -t nfs -o rsize=1048576,wsize=1048576 192.168.1.122:/nfs/share /mnt/nfs
[root@localhost ~]# grep /mnt/nfs /proc/mounts
192.168.1.122:/nfs/share /mnt/nfs nfs rw,relatime,vers=3,rsize=262144,wsize=262144,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=192.168.1.122,mountvers=3,mountport=892,mountproto=udp,local_lock=none,addr=192.168.1.122 0 0
</pre>
</p>
<p>但从上面的结果来看，实际使用的块大小还是 256KB。</p>
<p>在客户端这边修改 mount 参数将 NFS UDP 方式的读写块大小增加到 256KB：</p>
<p><pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# mount -t nfs -o udp,rsize=262144,wsize=262144 192.168.1.122:/nfs/share /mnt/nfs
[root@localhost ~]# grep /mnt/nfs /proc/mounts
192.168.1.122:/nfs/share /mnt/nfs nfs rw,relatime,vers=3,rsize=32768,wsize=32768,namlen=255,hard,proto=udp,timeo=11,retrans=3,sec=sys,mountaddr=192.168.1.122,mountvers=3,mountport=892,mountproto=udp,local_lock=none,addr=192.168.1.122 0 0
</pre>
</p>
<p>UDP 模式下读写的块大小也无法修改，客户端似乎已经使用了最大的读写块大小。</p>
<p>没办法，下面来看看 Linux kernel 源代码，找出真正的原因，先在 include/linux/nfs_xdr.h 文件中找到了 NFS I/O 块大小的定义：</p>
<p><pre class="brush: cpp; title: include/linux/nfs_xdr.h; notranslate">
/*
 * To change the maximum rsize and wsize supported by the NFS client, adjust
 * NFS_MAX_FILE_IO_SIZE.  64KB is a typical maximum, but some servers can
 * support a megabyte or more.  The default is left at 4096 bytes, which is
 * reasonable for NFS over UDP.
 */
#define NFS_MAX_FILE_IO_SIZE    (1048576U)
#define NFS_DEF_FILE_IO_SIZE    (4096U)
#define NFS_MIN_FILE_IO_SIZE    (1024U)
</pre>
</p>
<p>这里可以看到 NFS 默认使用 4KB 块大小，客户端实际挂载时会做调整，最小 1KB，最大 1MB。</p>
<p>NFS 客户端在挂载时会与 NFS 服务器协商适合的读写块大小值，我们来看看 fs/nfs/client.c 文件中协商设置 NFS 文件系统信息的代码：</p>
<p><pre class="brush: cpp; highlight: [0,11,12,13,14]; title: fs/nfs/client.c; notranslate">
static void nfs_server_set_fsinfo(struct nfs_server *server, struct nfs_fsinfo *fsinfo)
{
	unsigned long max_rpc_payload;

	/* Work out a lot of parameters */
	if (server-&gt;rsize == 0)
		server-&gt;rsize = nfs_block_size(fsinfo-&gt;rtpref, NULL);
	if (server-&gt;wsize == 0)
		server-&gt;wsize = nfs_block_size(fsinfo-&gt;wtpref, NULL);

	if (fsinfo-&gt;rtmax &gt;= 512 &amp;&amp; server-&gt;rsize &gt; fsinfo-&gt;rtmax)
		server-&gt;rsize = nfs_block_size(fsinfo-&gt;rtmax, NULL);
	if (fsinfo-&gt;wtmax &gt;= 512 &amp;&amp; server-&gt;wsize &gt; fsinfo-&gt;wtmax)
		server-&gt;wsize = nfs_block_size(fsinfo-&gt;wtmax, NULL);

	max_rpc_payload = nfs_block_size(rpc_max_payload(server-&gt;client), NULL);
	if (server-&gt;rsize &gt; max_rpc_payload)
		server-&gt;rsize = max_rpc_payload;
	if (server-&gt;rsize &gt; NFS_MAX_FILE_IO_SIZE)
		server-&gt;rsize = NFS_MAX_FILE_IO_SIZE;
	server-&gt;rpages = (server-&gt;rsize + PAGE_CACHE_SIZE - 1) &gt;&gt; PAGE_CACHE_SHIFT;

	server-&gt;backing_dev_info.name = &quot;nfs&quot;;
	server-&gt;backing_dev_info.ra_pages = server-&gt;rpages * NFS_MAX_READAHEAD;

	if (server-&gt;wsize &gt; max_rpc_payload)
		server-&gt;wsize = max_rpc_payload;
	if (server-&gt;wsize &gt; NFS_MAX_FILE_IO_SIZE)
		server-&gt;wsize = NFS_MAX_FILE_IO_SIZE;
	/*......*/
}
</pre>
</p>
<p>从上面 nfs_server_set_fsinfo 函数的代码可以看到 NFS 客户端实际参考了服务器返回的 rtmax 和 wtmax 值，而这个值可以在挂载 NFS 文件系统时用抓包工具看到（NFS 使用的 RPC 协议）。</p>
<p>下面的图片中显示的就是 NFS 客户端中指定 rsize 和 wsize 参数为 1MB 时 Wireshark 上抓到的 NFS FSINFO 请求的实际数据；</p>
<div style="width: 513px" class="wp-caption alignnone"><a href="http://zohead.com/wp-content/uploads/nfs-mount-cap.jpg" target="_blank"><img alt="挂载NFS的网络抓包" src="http://zohead.com/wp-content/uploads/nfs-mount-cap.jpg" width="503" height="231" /></a><p class="wp-caption-text">挂载NFS的网络抓包</p></div>
<p>上面图片里小椭圆圈表示的是 NFS FSINFO 请求，大椭圆圈里就是服务器传过来的 rtmax 和 wtmax 值了，我们可以看到值就是 256KB。这样也就能解释了为什么客户端增大 NFS 读写块大小也不起作用了。</p>
<p>我们后台登陆到 NFS 服务器上，可以从 /proc/fs/nfsd/max_block_size 文件中看到当前 NFS 服务器的最大块大小，然后尝试修改它：</p>
<p><pre class="brush: bash; title: ; notranslate">
~ # cat /proc/fs/nfsd/max_block_size
262144
~ # echo 524288 &gt; /proc/fs/nfsd/max_block_size
~ # cat /proc/fs/nfsd/max_block_size
262144
</pre>
</p>
<p>可以看到当前 NFS 服务器的最大读写块大小确实是 256KB，但是我们想修改它的值的时候，却似乎又修改不了。这样只能再看看修改 max_block_size 的 kernel 源代码了，对应的代码在 nfsd/nfsctl.c 文件中：</p>
<p><pre class="brush: cpp; highlight: [14,15,18]; title: nfsd/nfsctl.c; notranslate">
static ssize_t write_maxblksize(struct file *file, char *buf, size_t size)
{
	char *mesg = buf;
	if (size &gt; 0) {
		int bsize;
		int rv = get_int(&amp;mesg, &amp;bsize);
		if (rv)
			return rv;
		/* force bsize into allowed range and
		 * required alignment.
		 */
		if (bsize &lt; 1024)
			bsize = 1024;
		if (bsize &gt; NFSSVC_MAXBLKSIZE)
			bsize = NFSSVC_MAXBLKSIZE;
		bsize &amp;= ~(1024-1);
		mutex_lock(&amp;nfsd_mutex);
		if (nfsd_serv &amp;&amp; nfsd_serv-&gt;sv_nrthreads) {
			mutex_unlock(&amp;nfsd_mutex);
			return -EBUSY;
		}
		nfsd_max_blksize = bsize;
		mutex_unlock(&amp;nfsd_mutex);
	}

	return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, &quot;%d\n&quot;,
							nfsd_max_blksize);
}
</pre>
</p>
<p>write_maxblksize 函数中判断了传入的参数，如果写入的值超过 NFSSVC_MAXBLKSIZE 值则固定为 NFSSVC_MAXBLKSIZE 值，那我们来看看 NFSSVC_MAXBLKSIZE 的定义：</p>
<p><pre class="brush: cpp; title: linux/nfsd/const.h; notranslate">
/*
 * Maximum blocksizes supported by daemon under various circumstances.
 */
#define NFSSVC_MAXBLKSIZE   RPCSVC_MAXPAYLOAD
/* NFSv2 is limited by the protocol specification, see RFC 1094 */
#define NFSSVC_MAXBLKSIZE_V2    (8*1024)
</pre>
</p>
<p>linux/nfsd/const.h 中 NFSSVC_MAXBLKSIZE 定义为了 RPCSVC_MAXPAYLOAD 的值，那看看 linux/sunrpc/svc.h 中 RPCSVC_MAXPAYLOAD 的实际值：</p>
<p><pre class="brush: cpp; title: linux/sunrpc/svc.h; notranslate">
/*
 * Maximum payload size supported by a kernel RPC server.
 * This is use to determine the max number of pages nfsd is
 * willing to return in a single READ operation.
 *
 * These happen to all be powers of 2, which is not strictly
 * necessary but helps enforce the real limitation, which is
 * that they should be multiples of PAGE_CACHE_SIZE.
 *
 * For UDP transports, a block plus NFS,RPC, and UDP headers
 * has to fit into the IP datagram limit of 64K.  The largest
 * feasible number for all known page sizes is probably 48K,
 * but we choose 32K here.  This is the same as the historical
 * Linux limit; someone who cares more about NFS/UDP performance
 * can test a larger number.
 *
 * For TCP transports we have more freedom.  A size of 1MB is
 * chosen to match the client limit.  Other OSes are known to
 * have larger limits, but those numbers are probably beyond
 * the point of diminishing returns.
 */
#define RPCSVC_MAXPAYLOAD   (1*1024*1024u)
#define RPCSVC_MAXPAYLOAD_TCP   RPCSVC_MAXPAYLOAD
#define RPCSVC_MAXPAYLOAD_UDP   (32*1024u)
</pre>
</p>
<p>从 linux/sunrpc/svc.h 中可以看到 NFS 读写块大小必须为 2 的幂，这样也大概知道读写块大小限制的原因了：</p>
<p>对于 UDP 来说，由于一个 UDP 包最大才 64KB，因此使用 UDP 协议的 NFS 读写块大小最大不超过 48KB，而 kernel 中则直接限制为 32KB 了；而使用 TCP 协议的 NFS 由于没有这个限制允许更大的读写块大小，但 Linux kernel 还是将其限制为 1MB 了。</p>
<p>至于 max_block_size 值不能直接修改的现象也找到原因了，在 nfsd/nfsctl.c 文件中高亮显示的第 18 行代码里判断了 NFS 服务器是否在启动运行，如果在运行则不允许修改。</p>
<p>下面就好办了，先卸载 NFS 共享的挂载，停止服务器的 NFS 服务，修改 max_block_size 值，然后重新启动 NFS 服务，</p>
<p><pre class="brush: bash; title: ; notranslate">
~ # service nfs stop
Shutting down NFS mountd:                                  [  OK  ]
Shutting down NFS daemon:                                  [  OK  ]
Shutting down NFS services:                                [  OK  ]
~ # echo 1048576 &gt; /proc/fs/nfsd/max_block_size
~ # cat /proc/fs/nfsd/max_block_size
1048576
~ # service nfs start
</pre>
</p>
<p>可以看到现在 NFS 的最大块大小可以修改了，接着在客户端中指定读写块大小并重新挂载 NFS 共享，这个时候客户端也能正确使用更大的块大小了：</p>
<p><pre class="brush: bash; title: ; notranslate">
[root@localhost fs]# mount -t nfs -o rsize=1048576,wsize=1048576 192.168.1.122:/nfs/share /mnt/nfs
[root@localhost fs]# grep /mnt/nfs /proc/mounts
192.168.1.122:/nfs/share /mnt/nfs nfs rw,relatime,vers=3,rsize=1048576,wsize=1048576,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=192.168.1.122,mountvers=3,mountport=892,mountproto=udp,local_lock=none,addr=192.168.1.122 0 0
</pre>
</p>
<p>如果要深究一下 NFS 服务器初始的最大块大小只有 256KB 的原因，可以看看 kernel 中 nfsd/nfssvc.c 文件中的代码：</p>
<p><pre class="brush: cpp; highlight: [13,20,22,23,24]; title: nfsd/nfssvc.c; notranslate">
int nfsd_create_serv(void)
{
	int err = 0;

	WARN_ON(!mutex_is_locked(&amp;nfsd_mutex));
	if (nfsd_serv) {
		svc_get(nfsd_serv);
		return 0;
	}
	if (nfsd_max_blksize == 0) {
		/* choose a suitable default */
		struct sysinfo i;
		si_meminfo(&amp;i);
		/* Aim for 1/4096 of memory per thread
		 * This gives 1MB on 4Gig machines
		 * But only uses 32K on 128M machines.
		 * Bottom out at 8K on 32M and smaller.
		 * Of course, this is only a default.
		 */
		nfsd_max_blksize = NFSSVC_MAXBLKSIZE;
		i.totalram &lt;&lt;= PAGE_SHIFT - 12;
		while (nfsd_max_blksize &gt; i.totalram &amp;&amp;
		       nfsd_max_blksize &gt;= 8*1024*2)
			nfsd_max_blksize /= 2;
	}

	nfsd_serv = svc_create_pooled(&amp;nfsd_program, nfsd_max_blksize,
				      nfsd_last_thread, nfsd, THIS_MODULE);
	if (nfsd_serv == NULL)
		err = -ENOMEM;
	else
		set_max_drc();

	do_gettimeofday(&amp;nfssvc_boot);		/* record boot time */
	return err;
}
</pre>
</p>
<p>从上面的可以看到，NFS 服务器在决定默认的最大读写块大小时考虑到内存占用情况，每个 NFS 内核线程最多只使用 1/4096 的物理内存大小，对于物理内存超过 4GB 的机器才使用最大的 1MB 读写块大小。</p>
<p>来看看我们使用的 NFS 服务器的内存情况，可以看到服务器只使用了 2GB 的内存：</p>
<p><pre class="brush: bash; title: ; notranslate">
~ # free
             total       used       free     shared    buffers     cached
Mem:       2040272     335476    1704796          0       2360      70076
-/+ buffers/cache:     263040    1777232
Swap:            0          0          0
</pre>
</p>
<p>按照 nfsd/nfssvc.c 文件中的代码，i.totalram 实际值为所有可用的物理内存的页数量，我们这里就是 2040272 / 4KB（默认的 PAGE_SIZE 页大小），按照高亮的第 22 - 24 行代码计算出来的默认最大块大小值就是 262144 了。</p>
<p>本文为个人经过测试及分析 Linux kernel 源代码得出的分析结果，如果其中存在错误还请提出指正哦~~~ ^_^</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/nfs-rwsize/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>强制重启Linux系统的几种方法</title>
		<link>https://zohead.com/archives/linux-force-reboot/</link>
		<comments>https://zohead.com/archives/linux-force-reboot/#comments</comments>
		<pubDate>Sun, 26 Jan 2014 03:15:45 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[IPMI]]></category>
		<category><![CDATA[ipmitool]]></category>
		<category><![CDATA[kernel]]></category>
		<category><![CDATA[SysRq]]></category>
		<category><![CDATA[watchdog]]></category>
		<category><![CDATA[重启]]></category>

		<guid isPermaLink="false">http://zohead.com/?p=667</guid>
		<description><![CDATA[实际生产环境中某些情况下 Linux 服务器系统在出现致命错误需要远程进行重启，通过常规的 reboot、init 6 等方法无法正常重启（例如重启时卡在驱动程序里等情况），这时就需要通过下面介绍的几种特殊的方法进行强制重启。 注意 下面这些强制重启 Linux 的方法都是直接跳过 umount 文件系统及 sync 等操作，可能导致数据损坏，不在特殊情况下请勿使用。另外当然这些都是需要 root 超级用户权限的哦。 reboot 命令 直接通过运行 reboot -nf 命令，这样重启时可以指定跳过 init 的处理和 sync 操作，这样可以避免大多数情况下的问题。 magic SysRq [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>实际生产环境中某些情况下 Linux 服务器系统在出现致命错误需要远程进行重启，通过常规的 <code>reboot</code>、<code>init 6</code> 等方法无法正常重启（例如重启时卡在驱动程序里等情况），这时就需要通过下面介绍的几种特殊的方法进行强制重启。</p>
<blockquote>
<p><strong>注意</strong></p>
<p>下面这些强制重启 Linux 的方法都是直接跳过 umount 文件系统及 sync 等操作，可能导致数据损坏，不在特殊情况下请勿使用。另外当然这些都是需要 root 超级用户权限的哦。</p>
</blockquote>
<h2>reboot 命令</h2>
<p>直接通过运行 <code>reboot -nf</code> 命令，这样重启时可以指定跳过 init 的处理和 sync 操作，这样可以避免大多数情况下的问题。</p>
<h2>magic SysRq key 方法</h2>
<p>magic SysRq key 通过 proc 接口提供用户直接发底层命令给 kernel 的功能，可以实现关机、重启、宕机等操作，Linux kernel 需要开启 <code>CONFIG_MAGIC_SYSRQ</code> 才可以支持 magic SysRq key。</p>
<p>运行下面两条命令就可以直接强制重启系统：</p>
<pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# echo 1 &gt; /proc/sys/kernel/sysrq
[root@localhost ~]# echo b &gt; /proc/sysrq-trigger
</pre>
<p>相应的直接强制关机的命令：</p>
<pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# echo 1 &gt; /proc/sys/kernel/sysrq
[root@localhost ~]# echo o &gt; /proc/sysrq-trigger
</pre>
<h2>watchdog 方法</h2>
<p>如果 Linux kernel 未开启 magic SysRq key 或者不起作用，可以尝试使用 watchdog 重启方法。watchdog 通过监控数据输入是否正常可以实现在系统出现异常时自动重启系统，这里我们刚好可以借用的。</p>
<p>首先需要加载 watchdog 支持，这个和主板硬件设备有关，如果只需要软件模拟的，可以运行：</p>
<pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# modprobe softdog
</pre>
<p>命令加载软件 watchdog 支持，接着再运行：</p>
<pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# cat /dev/watchdog
</pre>
<p>命令，该命令会马上退出并报错，同时系统日志中就会提示：</p>
<pre class="brush: plain; title: ; notranslate">
softdog: Unexpected close, not stopping watchdog!
</pre>
<p>这就表示 watchdog 设备是被意外关闭的而不是正常停止的，大约等待 60 秒之后你就会发现 Linux 系统自动重启了。Linux watchdog 的异常等待时间是通过 <code>/proc/sys/kernel/watchdog_thresh</code> 设置的，一般默认为 60 秒。</p>
<h2>IPMI 方法</h2>
<p>上面几种方法都不能用？如果你的主板刚好支持 IPMI 管理接口的话，那可以直接通过 IPMI 实现硬件上的强制关机或重启。</p>
<p>首先加载 IPMI 支持：</p>
<pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# modprobe ipmi_msghandler ipmi_devintf ipmi_si
</pre>
<p>确认 IPMI 设备是否已找到：</p>
<pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# ls -l /dev/ipmi*
</pre>
<p>如果输出正常的话表示 IPMI 被正确加载了，接着安装 <code>ipmitool</code> 管理工具。<code>ipmitool</code> 可以通过 IPMI 接口完成对本机或远程主机的一系列管理操作。</p>
<p>这里我们就用直接电源管理的，重启系统：</p>
<pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# ipmitool power reset
</pre>
<p>运行完成后主机就会马上重启，相应的关闭主机可以运行命令：</p>
<pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# ipmitool power off
</pre>
<p><code>ipmitool</code> 还可以实现在系统未启动时远程查看监控主板硬件状态等功能，在 IPMI 可用的情况下 <code>ipmitool</code> 还是比较方便好用的。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/linux-force-reboot/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>构建Lava XOLO X900非官方kernel</title>
		<link>https://zohead.com/archives/xolo-x900-kernel/</link>
		<comments>https://zohead.com/archives/xolo-x900-kernel/#comments</comments>
		<pubDate>Thu, 20 Dec 2012 13:38:43 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[kernel]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[手机]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[Atom]]></category>
		<category><![CDATA[cifs]]></category>
		<category><![CDATA[config]]></category>
		<category><![CDATA[Intel]]></category>
		<category><![CDATA[K800]]></category>
		<category><![CDATA[Lava]]></category>
		<category><![CDATA[OTG]]></category>
		<category><![CDATA[U盘]]></category>
		<category><![CDATA[x86]]></category>
		<category><![CDATA[X900]]></category>
		<category><![CDATA[XOLO]]></category>
		<category><![CDATA[Z2460]]></category>
		<category><![CDATA[印度]]></category>
		<category><![CDATA[模块]]></category>
		<category><![CDATA[联想]]></category>

		<guid isPermaLink="false">http://zohead.com/?p=371</guid>
		<description><![CDATA[本文同步自（最佳显示效果请点击）：https://zohead.com/archives/xolo-x900-kernel/ 本文目标为 Lava XOLO X900 这一印度咖喱味手机，同样 Intel Atom Z2460 的芯，移植 kernel 的方法和之前的联想 K800 手机基本一致，具体请移步下面的链接： https://zohead.com/archives/k800-kernel-otg-udisk/ 经过确认 XOLO X900 默认也是 Android 4.0.4 ROM，同样 3.0.8 kernel，当然硬件会有所不同，不过 OTG U盘所需要使用的 usb-stor [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>本文同步自（最佳显示效果请点击）：<a href="https://zohead.com/archives/xolo-x900-kernel/" target="_blank">https://zohead.com/archives/xolo-x900-kernel/</a></p>
<p>本文目标为 Lava XOLO X900 这一印度咖喱味手机，同样 Intel Atom Z2460 的芯，移植 kernel 的方法和之前的联想 K800 手机基本一致，具体请移步下面的链接：</p>
<p><a href="https://zohead.com/archives/k800-kernel-otg-udisk/" target="_blank">https://zohead.com/archives/k800-kernel-otg-udisk/</a></p>
<p>经过确认 XOLO X900 默认也是 Android 4.0.4 ROM，同样 3.0.8 kernel，当然硬件会有所不同，不过 OTG U盘所需要使用的 usb-storage 模块也是和 K800 一样默认没有开启的。</p>
<p>基于基本相同的 kernel source，先使用 Medfield 默认的配置 i386_mfld_defconfig，发现与现有的 kernel 差距比较多，然后用 Moto 的 i386_mfld_moto_defconfig 编译出的模块加载依然 panic。没办法，硬着头皮与实际运行中 kernel 情况进行对比，发现基于默认配置 i386_mfld_defconfig 进行修改是比较好的方法，而且看得出 XOLO 相对联想还是做了一定的优化，去掉了像 CONFIG_DEBUG_KERNEL 之类的很多调试选项，默认允许了 tun 驱动等。另外声卡相关的配置不同的地方也比较多，经过最终改动和确认，编译之后，先已能成功加载并使用之前在 K800 上编译的那些模块。</p>
<p>XOLO X900 的内核模块和配置文件的下载地址：</p>
<p><a href="http://miseal.googlecode.com/files/x900-kernel-config-modules.7z" target="_blank">http://miseal.googlecode.com/files/x900-kernel-config-modules.7z</a></p>
<p>使用方法和 K800 的基本一致，只是 CIFS 文件系统的使用上有变动：</p>
<p>insmod des_generic.ko<br />
insmod md4.ko<br />
insmod cifs.ko</p>
<p>ASIX Electronics 的 USB 以太网卡的驱动 asix.ko 也已经更新到官方最新的版本（kernel 自带的版本无法使用）。</p>
<p>现已基本可以确认我改动之后的 kernel 和 X900 现有 kernel 的相似性已有 90% 或以上了，不过由于 X900 手机悲剧滴将 bootloader 给锁了，无法刷未签名的 kernel，暂时还没有太好的办法来直接替换 kernel 进行测试哦。</p>
<p>另外有一些需要说明的地方：</p>
<ol>
<li>XOLO X900 现有 kernel 中支持 Kineto GAN 虚拟网卡驱动（支持 VOIP 网络电话的），Moto Razr i 公开的 kernel source 中却并没有这个的支持，为此我手工增加了这个的驱动（上面下载的 kernel configuration 中也有的）；</li>
<li>目前还有 apwr3_0、pax、sep3_7 这三个非免费开源的内核模块没有源代码，暂时也没有办法在网上找到，不过从现在来看，似乎 X900 实际运行时也未使用这三个模块。</li>
</ol>
<p>同样我不对任何导致你的手机 panic 挂掉的后果承担责任，有任何问题欢迎提出指正哦 ^_^</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/xolo-x900-kernel/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>密码保护：使用DM thin-provisioning提高存储利用率</title>
		<link>https://zohead.com/archives/dm-thin-provision/</link>
		<comments>https://zohead.com/archives/dm-thin-provision/#comments</comments>
		<pubDate>Fri, 14 Dec 2012 15:35:38 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Device mapper]]></category>
		<category><![CDATA[kernel]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[存储]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[DM]]></category>
		<category><![CDATA[dmsetup]]></category>
		<category><![CDATA[LVM]]></category>
		<category><![CDATA[LVM2]]></category>
		<category><![CDATA[thin]]></category>
		<category><![CDATA[回滚]]></category>
		<category><![CDATA[快照]]></category>
		<category><![CDATA[自动精简配置]]></category>

		<guid isPermaLink="false">http://zohead.com/?p=356</guid>
		<description><![CDATA[无法提供摘要。这是一篇受保护的文章。]]></description>
				<content:encoded><![CDATA[<form action="https://zohead.com/wp-login-zohead.php?action=postpass" class="post-password-form" method="post">
<p>这是一篇受密码保护的文章，您需要提供访问密码：</p>
<p><label for="pwbox-356">密码： <input name="post_password" id="pwbox-356" type="password" size="20" /></label> <input type="submit" name="Submit" value="提交" /></p>
</p></form>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/dm-thin-provision/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
