Monthly Archives: October 2015

[翻译] 非易失性内存上的文件系统支持

原文: Supporting filesystems in persistent memory September 2, 2014

前言:在今年的CLSF会议上,一个NVDIMM的话题非常火爆,大家对这种可以把内存当硬盘用的设备都很感兴趣。同时上游内核也在做DAX相关的东西,以后RHEL7上可能也是要支持的。我就趁着热呼劲儿再多看看相关文章了。

正文:
近几年来,一直有传言说非易失性内存设备(non-volatile memory – NVM)将会改变我们使用系统的方式。这种设备能够提供大量(也许是TB级别)的非易失的、能以内存的速度进行访问的存储空间。这么大量的非易失性内存可以用来做什么现在还不是很清楚,但是NVM已经引起越来越大的关注了。可能我们会在NVM上运行传统的文件系统——但是这些文件系统需要一些修改从而让用户能够得到NVM的全部性能优势。

在NVM上加上一层块设备驱动而让NVM设备看起来像普通存储设备是很容易的事情。但是,这样做会强制所有数据和系统page cache之间进行复制。既然NVM设备是可以直接访问的,那么这些复制就是效率低下的。对性能敏感的用户会尽可能的避免使用page cache来达到NVM设备的最高速度。

事实上从2005年开始,随着ext2加入了excute-in-place(XIP)支持,内核就已经对NVM设备直接访问有一定支持了。这些代码可以把可以直接寻址的设备映射到用户空间,这样文件数据就不需要通过page cache来访问了。但是使用XIP的人很少,这些代码已经多年没有更新了,并且无法在现在的文件系统上工作。

去年,Matthew Wilcox开始着手改进XIP代码,试图把它和ext4文件系统整合在一起。在整理的过程中,他发现XIP无法和同时代的文件系统很好的结合在一起,并且代码里还有很多恼人的race condition。所以,随后他的工作从改进XIP转向了代替它。这项工作由21个patch组成,受到越来越多的关注。现在已经接近完成,可以并入主线内核了。

这些patch使用了一个叫做DAX(很显然代表“direct access”)子系统代替了XIP。在块设备层上,它用以下函数替换掉了struct block_device_operations中的direct_access()函数

    long (*direct_access)(struct block_device *dev, sector_t sector,
			  void **addr, unsigned long *pfn, long size);

这个函数接收sector号和size作为参数来说明调用者想访问多少字节的数据。如果给定的空间是可以直接访问的,那么基地址(内核地址)就通过addr返回,同时相应的页帧号(page frame number)也保存在pfn中。当从用户空间直接访问这些内存的时候,页表会使用这个页帧号。

这种使用页帧号和地址的方法可能有些奇特,内核大部分时候通过struct page来处理这一级别的内存。但这在DAX的情况下是不可行的,因为一个简单的原因:非易失性内存不是普通的内存,没有和它们相关联的页结构体(page structure)。缺少页结构体产生了很多后果,也许最重要的一个是这些NVM不能被其他设备用来做DMA访问。所以在网络设备和存储设备之间做零拷贝(zero-copy)传输,像这样的操作在NVM设备上就无法实现了。Boaz Harrosh正在写一些patch来增加NVM的page structure的支持,但这项工作还处于一个早期阶段。

再把目光移向IO栈的更上一层,NVM的支持在VFS层已经做了很多所有文件系统都能用到的工作。新建了各种通用的帮助函数(helpers)用以辅助一些常见的操作(reading, writing, truncating, memory-mapping等等)。在大部分情况下,文件系统只需要在可以使用DAX的inode中标记一个新的S_DAX标志然后在正确的地方调用帮助函数就可以了。(一点点)更多的信息可以参考patchset中的文档。这个patchset在ext4中加上了一些必要的支持。

但是Andrew Morton针对这项工作有一些疑问。最主要的问题是,为什么不直接使用一个经过修改的内存文件系统(ramfs, tmpfs)?这看上去是一个很好的问题,因为这些文件系统已经是为可以直接访问的内存设计的并且做了很多优化。但问题也在这,ram-based文件系统是为ram设计的,并不能很好的适应NVM设备。

来自Dave Chinner的信对此做了详细的解释,而且这封信很值得一读。大体上概括起来说,问题归结到以下这一点上:ram-based文件系统并没有针对数据持久性做任何设计。它们在每次启动的时候都是一个全新的文件系统,并不需要处理上一次系统运行遗留下来的东西。但在另一方面,NVM需要处理更多数据持久性相关的问题,比如在NVM上存储的数据在机器重启之后也不会丢失,并且需要在系统崩溃的时候足够健壮,在升级内核之后数据也是需要依然存在的等等。这些需求ram-based文件系统都无法满足。

所以,NVM文件系统需要所有传统文件系统所拥有的工具,用来来识别磁盘上的文件系统,做检查并处理文件系统损坏的情况。为了保证持久存储上的数据一直处于一致的状态,NVM也需要各种传统文件系统所使用的技术。例如,元数据写入的顺序必须非常仔细的处理并且用屏障来保护。因为在不同内核之间的兼容性是很重要的,所以内核中的数据结构不能直接存储在文件系统中,数据必须在host format和on-disk format之间来回转换。这些都是传统文件系统做了而ram-based文件系统没有做的事情。

然后,Dave解释道,在可扩展性方面还存在一些问题:

更进一步,NVM文件系统需要可以被扩展到非常大的存储上。在不久的将来机器上会有好几十TB的NVDIMM存储容量,所以对空闲空间的管理和对已使用空间的分配/释放的并发操作,这些都会是影响NVM文件系统性能的关键问题。所以最后你需要使用块/分配组来把空间做进一步划分。这看起来很像XFS和EXT4现在做的。

然后你需要所有的索引扩展到千万级别。至少是千万级别的,其实更可能是亿或者十亿级别。因为存储好几十TB的小文件就需要对数十亿的文件进行索引。而且因为这样做基本上没有性能上的惩罚,人们就会把文件系统当作一个大的数据库来用。所以现在你又需要有一个可扩展的并且和posix兼容的文件夹结构,可扩展的空闲空间索引,动态的、可扩展的inode分配/释放操作等等。噢,为了处理机器上的上百个cpu核还需要高并发性。

作为结论,Dave指出现在内核已经有不少满足上述需求的“非易失存储实现”了,那就是XFS和EXT4文件系统(虽然Dave一直认为ext4的可扩展性有问题)。现在这两种文件系统都能够在非易失性内存的块设备上工作。但最大的问题就是没有一种方法能够让用户直接访问数据而不需要经过page cache,这就是DAX所需要提供的功能。

现在有一些工作组在从头为NVM设计文件系统。但这些工作基本都还处于早期阶段,在内核邮件列表里还没有人发出patch来,更没有申请合并到主线内核的。所以想充分利用NVM性能的用户,在未来几年是不会从这些工作上得到什么帮助了。所以说,在目前已有的文件系统上增加NVM支持是一个真实的需求,得出这样的结论也并不是不合理的。

目前来看DAX就是通往这个目标的一条路。现在所需要做的就是尽快review这些patch并让patch达到让所有相关子系统的维护者都同意合并的状态。review工作目前进行缓慢,因为patch很复杂并且修改了很多不同的子系统。但是,作为对之前review的回应,patch已经有了很多的改进,并且看起来离最终的版本不远了。也许在不久的将来DAX功能最终就会进入主线内核。

OpenVPN over stunnel配合dnsmasq+dnscrypt实现最佳上网体验

(首先还是要唠叨几句,说一下折腾这套东西的前因后果和感受,然后再写具体部署和配置内容。)

随着墙的不断升级,以前的一些方法逐渐不稳定了。我一开始只是用ssh tunnel,这样浏览器配合autoproxy/foxyproxy/switchyomega使用简单方便。后来ssh tunnel被干扰的很不稳定了,经常无响应。而且这个方法也不能用于手机。后来结识了神器shadowsocks,在手机上很欢快的用了一段时间,但最近发现ss也不是那么稳定了,尤其是手机网络,很多时候无法连接,而且iphone上不越狱的话基本无法使用ss(只有一个浏览器样式的应用,不是全局的)。然后就折腾过各种vpn,先是OpenVPN,但也知道这东西用不长久,果然一个月不到就废掉了。也试过l2tp/ipsec等vpn,但速度很慢。最后用了ocserv+openconnect的方案,用了也一个月吧,然后又开始经常连不上,或者连上之后不长时间就无法使用了。

到此,我想得另外想办法了。一开是想的是写个私有协议,其实翻墙这东西就是把自己的数据加密传给国外的代理服务器帮忙转发,结果经过加密再传回来。但自己写毕竟麻烦,虽然已经有人这么做过了。经过搜索发现了stunnel这个好东西,就是干加密转发这件事的。然后在vps上搭了一个squid一个socks服务器,stunnel负责把收到的请求往这两个服务上转发,本地也起一个stunnel监听本地的请求然后发给远端的stunnel。手机的话就用SSLdroid来转发,其实就是手机上的stunnel。这个办法到现在还挺稳定的,就是iphone上没有stunnel类的软件,如果要用就得国内再找个服务器做转发。由此就引出了现在折腾完的这一套东西。(好长的缘由……)

这件事从十月12号开始想,然后搜了搜国内的vps提供商,没什么靠谱的。又看了看阿里云,最便宜的50一个月,对我这个简单的需求来说太贵了。最后想到用自己家里的机器吧,开个DMZ或者端口转发配合动态dns就好了。先试验了DMZ和动态DNS发现能用,就开始找机器了。一开始想用自己的台式机,但常年开机噪音大还不环保。后来想到09年做GSoC的时候折腾过的龙芯盒子,拿来折腾一番终于把龙芯盒子跑起来了。这下东西都全了,开始折腾吧!

在google的过程中想,如果能把OpenVPN流量也用stunnel转发(之前试了转发ocserv的流量,认证能开始,但认证过后就无法连接到服务器,不知道为啥)就方便很多了,手机上又可以用OpenVPN了,不用每个程序设置代理了(而且好多app还不能设置代理)。试了下发现是可行的。搜索的过程中又发现了dnsmasq, dnscrypt, chnroutes, gfwlist2dnsmasq等等各种好东西。最后就形成了现在这个最终的方案。

正文开始!

基本部署

需要国外一台VPS和国内一台机器,简单起见就分别命名remote_server和local_server吧。

基本思路就是,remote_server上运行openvpn server,只监听本地请求(localhost),运行stunnel负责把openvpn的请求转发给本地的openvpn server,同时在local_server上起一个stunnel监听openvpn的连接请求,然后转发给remote_server的stunnel,其实就是把openvpn流量封装到了stunnel里穿墙出去。

然后使用chnroutes项目里得到的国内ip段的路由表,把这些路由加入到openvpn client的配置文件里,这样国内ip不走vpn,国外ip都走vpn,加快国内网站的速度。

最后在local_server上启用dnsmasq负责openvpn的域名解析请求,这样做的好处是:一是避免dns污染,把gfwlist里的域名(通过gfwlist2dnsmasq项目)请求通过dnscrypt转发出去;二是对于国内有cdn的服务可以把地址解析到国内的cdn上,上网会快很多,否则所有dns都在国外解析,很可能返回的cdn地址在国外或者跟你在不同网络(联通/电信)。

配置过程

配置OpenVPN Server

这一步我用的是有人写好的一个脚本,openvpn-install,运行之后根据提示一步步设置就好了,很简单。只是在服务器地址那个地方需要填写local_server的地址,因为最后会自动生成客户端配置文件,但客户端直连的是local_server。然后协议选择tcp,udp不可以,因为stunnel还只能准发tcp协议。最后编辑 /etc/openvpn/server.conf,做如下修改

 - push "dhcp-option DNS xxx.xxx.xxx.xxx"
 + push "dhcp-option DNS your_local_server_address_ip"
 - keepalive 10 120
 + keepalive 30 300

第一个修改是把local_server的ip当作dns推送给client,好使用我们自己的dnsmasq服务。第二个修改是把心跳时间变长30秒一次,并且300秒无心跳才认为断开了连接,这个是因为最后要添加的国内路由表比较大,会用比较长的时间,120不够长,超时之后server会认为连接失败然后重连,其实我们只是在设置路由表……其实也可以在openvpn server端推送路由表,但这样做连接的时间就更长了。

注意这里dns地址必须是ip,如果像我这样用的是动态域名绑定ip的,那么只能写个脚本监控这个ip的变化了……我就把下边这个脚本放在remote_server里的crontab了

#!/bin/bash
if [ `id -u` -ne 0 ]; then
        echo "Need root"
        exit 1
fi

ovpn_conf_file=/etc/openvpn/server.conf
latest_ip=`dig your.localserver.ddns | grep "^your.localserver" | awk '{print $5}'`
current_ip=`grep "push.*dhcp-option" $ovpn_conf_file | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+"`

if [ "$latest_ip" != "$current_ip" ]; then
        sed -i -e "s/^push.*dhcp-option.*/push \"dhcp-option DNS $latest_ip\"/" $ovpn_conf_file
        service openvpn restart
fi
exit 0

配置stunnel

remote_server:

debug = 3
fips = no
pid =
socket = l:TCP_NODELAY=1
cert = /path/to/stunnel.pem
CAfile = /path/to/client.pem
verity = 3

[openvpn]
accept = 3333
connect = localhost:4444

基本意思就是监听3333端口,转发给本地4444端口,修改端口为你自己的配置。

local_server:

debug = 3
fips = no
pid =
socket = l:TCP_NODELAY=1
cert = /path/to/client.pem

[openvpn]
client = yes
accept = 4444
connect = remote_server_address:3333

监听本地4444端口,转发到remote_server的3333端口。

关于stunnel的配置和证书的生成,我参考的是这几篇文章
[1] 通过 stunnel 搭建安全高性能的 sockts 代理服务器
[2] 使用dante-server和stunnel搭建socks代理服务器
[3] SSL HOWTO: USING OPENSSL TO GET KEYS INTO PKCS#12 FORMAT.

配置dnsmasq和dnscrypt-proxy

这两个服务都是在local_server上配置的。我用的是debian,安装dnsmasq和dnscrypt-proxy就可以了,但fedora上好像没有dnscrypt这个包,估计要自己编译安装了。

dnsmasq的配置很简单,而且配置文件里的注释说的也很清楚(就是很多……)。其实我就用了这么两行配置

interface=eth0
no-dhcp-interface=eth0
conf-dir=/etc/dnsmasq.d/,*.conf

大意就是在eth0上启用dns服务,不需要dhcp服务,并且包含/etc/dnsmasq.d/目录下的所有conf文件。

然后git clone gfwlist2dnsmasq项目,照着文档生成一份no_ipset的conf文件,然后copy到/etc/dnsmasq.d/目录就完活了。

除了gfwlist2dnsmasq之外,还看了看dnsmasq-china-list这个项目,但自己维护的域名列表gfwlist已经有一份了,而且感觉很全,还是通过gfwlist来生成感觉更好一些。

dnscrypt-proxy的配置也简单,修改配置文件,把端口改成非53,因为53端口dnsmasq已经用了。debian上是 /etc/default/dnscrypt-proxy,如果用的是systemd,还需要修改 /lib/systemd/system/dnscrypt-proxy.socket里的端口设置,然后还需要运行 systemctl daemon-reload才能生效。其他什么配置都不需要修改。

关于dnsmasq和dnscrypt的参考文档
[1] 使用一台Raspberry Pi作为家庭网关, 无障碍翻墙
[2] 使用国外 DNS 造成国内网站访问慢的解决方法
[3] 拒绝 DNS 污染,将 DNSCrypt 部署到 Openwrt 路由器上
[4] VPS 教程系列:Dnsmasq + DNSCrypt + SNI Proxy 顺畅访问 Google 配置教程

客户端配置

我这里客户端只是安卓手机,笔记本/台式机上我不需要全局vpn,浏览器有socks代理就够用了。安卓上装了官方的OpenVPN Connect,发现导入有大量路由设置的配置文件有问题,不是嫌文件太大就是设置路由的时候崩溃。最后我安装了OpenVPN for Android这个app,很好用,推荐!

这里需要配置的就是把第一步设置openvpn server的时候生成的ovpn配置文件更新一下,加入国内不走vpn的路由设置。这里用到了chnroutes这个项目,使用很方便,照着文档生成路由信息列表,然后直接cat追加到ovpn配置文件里就好了。手机客户端上导入这个配置,连接(可能会有点慢,因为路由条目很多)。

路由设置这块儿费了好大的劲儿。一开始想从server推送路由,结果官方openvpn app不支持太多路由条目(反正1000都不支持,而这个路由表有5000+行),换成openvpn for android之后发现推送太慢。但用server推送有个好处是当路由表变化的时候更新方便,在server上再弄一个crontab任务更新就好了。鉴于国内路由信息一般变化不大,就还是在客户端手动设置吧。

另外还试了一个bestroutetb项目里的路由,据项目说是根据chnroutes优化得来的,将路由条目降到了1000+,但我发现这个路由不准确,instagram基本不能用,很多图片刷不出来。所以最后还是用了chnroutes里的路由,因为这个是从APNIC的定义转换而来的。

测试

连接vpn,过程可能会有点慢。连上之后twitter,facebook,instagram都能顺利访问了,开微博看视频也很快,因为是国内的dns解析的地址走的也是国内的路由。这下做到只在必要的时候使用vpn了。

最后说一下,其实这已经不是为了用什么被墙的服务而折腾了,其实我常用的就是twitter,设置个代理完全能用,ss大部分时间也是很好用的。现在是完全就是看墙不爽,我一定要解决这个问题才折腾的。最后照例祝老校长长命百岁!

龙芯盒子上安装debian系统

(这个可得记录一下,折腾死我了)

想在家里弄一个常年开机的服务器,运行一些用于众所周知的原因的服务。然后就想起了几年前折腾过的龙芯盒子。具体来说是福龙盒子,cpu是龙芯2f。

盒子自带的系统有两个,一个是华镭(Rays),一个是Debian。两个系统都很老了,华镭已经停止支持了,网站都打不开了……更别说从源安装软件了。debian好像还是debian 5(lenny),网上找的lenny的源里的东西都不全了,很多软件404 not found,但是还依赖它们(debian官方的源也一样)。所以无法升级无法安装软件,我需要的stunnel虽然能装上,但是运行起来报错(local socket error),我也不知道为什么。

其实盒子里还有另外一个opensuse的系统,是我六年前折腾过的,从opensuse源代码开始交叉编译出来的。试着在这个系统上从源码编译stunnel,结果系统卡在了编译的过程中,什么错也没报,就那么静静的停在那了……自己折腾的系统还是不够稳定啊。

没办法,网上继续找其他系统。先后试了fedora的mips移植版(不支持2f cpu)和debian(debian的wiki里给的kernel也不支持2f……),都以失败告终。最后终于让我找到了社区版龙芯系统的老巢,找到了一份安装说明。剩下的就简单了,照着做吧,有人给做好了系统镜像,比自己折腾debian系统方便多了。我顺便还升级了pmon。

这里可以提一句的是,anheng.com.cn我这里访问很慢,可以从中科大的镜像里下载。装系统的时候会把整个硬盘都重新格式化(我自己做的系统也就这么没了……),root默认的密码一猜就猜出来了,就是loongson。进入系统之后修改源为

deb http://ftp.cn.debian.org/debian/ stable main contrib non-free
deb-src http://ftp.cn.debian.org/debian stable main contrib non-free

升级系统之后我又换成testing了,滚动升级能好一些,怕以后一下跨大版本升级会有问题。

安装配置了下stunnel,很好用!

Monthly Pic 2015 09

IMG_20150901_205705
九月假期多,回家次数就多,火车站人也多,差点没赶上

IMG_20150904_174734
回家吃到了日思夜想的烤肉

IMG_20150906_164703
沈阳故宫外的牌楼,故宫虽然离家不远,但从来没进去过,一直是外边溜达