Category Archives: Uncategorized

multi-instance udp2raw configuration for openwrt

/etc/config/udp2raw

config udp2raw foo
        option enable '1'
        option run_command '-c -l 127.0.0.1:55820 -r foo_ip:80 -k <bar_password> --raw-mode faketcp -a'

config udp2raw bar
        option enable '1'
        option run_command '-c -l 127.0.0.1:55821 -r bar_ip:80 -k <bar_password> --raw-mode faketcp -a'

/etc/init.d/udp2raw

#!/bin/sh /etc/rc.common

USE_PROCD=1

START=99
STOP=01

PROG=/usr/bin/udp2raw

_log() {
        logger -p daemon.info -t udp2raw "$@"
}

_err() {
        logger -p daemon.err -t udp2raw "$@"
}

start_service() {
    config_load "udp2raw"
    config_foreach start_instance udp2raw
}

start_instance() {
    config_get run_command "$1" 'run_command'
    _log "start instance $1 with $run_command"
    procd_open_instance "udp2raw_$1"
    procd_set_param command $PROG
    procd_append_param command $run_command
    procd_set_param stdout 1
    procd_set_param stderr 1
    procd_close_instance
}

reload_service() {
    stop
    start
}

service_triggers() {
    procd_add_reload_trigger udp2raw
}

Now, service udp2raw start

原神 x 编程: 基于丘丘语的编程语言MITA

各位旅行者好~ Olah Odomu!

著名丘丘语言学家,艾拉马斯克,在近日的研究中发现丘丘人正在通过一种特殊的编程语言试图重新控制提瓦特大陆上的遗迹守卫。他们的目的尚不明确,且此语言仍是草稿阶段,因此,暂时定名为 MITA ( Machine Instruction for Teyvat Automaton )意为“提瓦特自律机关机器指令”。

艾拉马斯克试着将其抄写出来并使用了地球科技 Go 语言进行了实现并分享在了 Github 上。

https://github.com/mitalang/mita

https://ngabbs.com/read.php?tid=39515586

例如,布尔值:

真(肯定) da
假(否定) nye

部分自然数

1 unu
2 du
3 unu du
4 du du
5 mani

由于丘丘语没有很多人类自然语言对应的抽象概念,而 “多个丘丘语单词可以组合成一个词组,以表达新的概念。丘丘语还有类似古汉语的词类活用现象,一个单词在不同语境下可以作名词或形容词”[1],我们可以对需要构建的词做出组合调整。因此该编程语言可能会在丘丘语研究员“艾拉 马斯克”有新研究后给出相应调整。

  • upa 汇聚, 即LISP中的“拼接”关键字 cons
  • muhe 想要,喜欢,即“想要特定功能”,可以设定成函数定义的抽象定义(defn
  • lawa 首领,可推导出“第一个”的意思,名词做动词,“取第一个”
  • kucha 弱小,既然不是第一,那就是剩下的东西,“取剩下的”
  • celi 火,因celi upa指代太阳,名做动理解成“升起”,可代替数学的“加”
  • movo 风、移动,可代替数学的“减”
  • shato 相似?,四舍五入就是等于
  • nyeshato 否定+相似(自造),不等于
  • abaabashato,时间在之前,套用过来就是小于,加上shato就是小于等于
  • untauntashato,时间在之后,套用过来就是大于,加上shato就是大于等于

她表示MITA中语法部分最独特是 lakucha ,大家都知道 lawa 在丘丘语中为首领的意思,而 kucha 为弱者(引申为随从),而当 lakucha 组合起来后,就形成了先取第一个元素( sada )再取后面的元素。

(lalalakukucha '((1 2) (3 4) ((5 6)) (7 8)))

将返回 5

其他更有趣的例如斐波那契数列,但由于丘丘语没有 0 的表达,因此艾拉本人还是使用了人类的 0.

(muhe(
        (yafib (mita (si)
                (dala ((shato si 0) 0)
                        (da (dala ((aba si du) unu)
                                (da (celi (yafib(movo si du)) (yafib(movo si unu))))
                        ))
                )
        ))
))

好了,目前就是艾拉的发现的MITA语言,欢迎大家移步项目地址进行讨论,mita dada!

ESXI 6.7 安装RAID1记录

因为最近树莓派的nVME因为突然断电数据损坏了,所以捡个垃圾的块带电池的RAID 卡,给自己的esxi机器整上,毕竟现在重要数据都在上面了……总不能真的All in one,断电后就全部嗝屁了吧。买了2块1TSSD,组个SAS 8087 RAID1。配置如下:

  • CPU:10 CPUs x Intel(R) Xeon(R) W-2150B CPU @ 3.00GHz
  • 主板:Supermicro X11SRM-F 单路
  • RAID卡:Adaptec 6805T 512MB Cache 6G SAS
  • ESXI:ESXI6.7

开机后按Ctrl+A,开启RAID BIOS

选择Create Array,设定对应的RAID类型,我这里选了RAID1

注意这里一定要启用两个Cache,要不然速度只有20M/s。保存并重启后,你会发现ESXI6.7 还识别不出来,这是因为没有驱动,得自己装……一顿搜索才找到,为了以后哪个倒霉蛋不要跟我一样找半天,我先扔这里了aacraid-6.0.6.2.1.57013-11 959565.zip。这下终于出来了。

倒是装上RAID之后,我发现磁盘性变得很奇怪……

小文件读写特别差,但是大文件又爆炸的好,我测试的是(1G文件读写,RAID才512M缓存),vmfs 6的磁盘格式(块1M),调整了RAID的读写模型成OLTP/DB 反而更差,希望有人能指出为啥

受不了老RAID的性能了,全部换成LSI2308 的raid卡了,但是性能惨不忍睹,BIOS自带的设置里并没有WriteCache,一顿搜索后发现了这个宝藏文章和lsiutil这个工具,可以拿来开启LSI RAID的写缓存!不过这个预先要求有mpt2sas这个驱动。

先安装mpt2sas,允许安装社区的驱动,使用下面的ssh命令,注意:必须用全路径(esxi装软件奇怪的要求)

esxcli software vib install -v <到驱动的全路径>/scsi-mpt2sas-20.00.01.00-1OEM.550.0.0.1331820.x86_64.vib

Installation Result
   Message: The update completed successfully, but the system needs to be rebooted for the changes to be effective.
   Reboot Required: true
   VIBs Installed: Avago_bootbank_scsi-mpt2sas_20.00.01.00-1OEM.550.0.0.1331820
   VIBs Removed: VMW_bootbank_scsi-mpt2sas_19.00.00.00-2vmw.670.0.0.8169922
   VIBs Skipped:

安装好,重启后,使用./lsiutil来变更writecache设置


LSI Logic MPT Configuration Utility, Version 1.71, Sep 18, 2013
sh: /sbin/modprobe: not found
mknod: /dev/mptctl: Function not implemented

1 MPT Port found

     Port Name         Chip Vendor/Type/Rev    MPT Rev  Firmware Rev  IOC
 1.  ioc0              LSI Logic SAS2308 D1      200      14000700     0

# 输入21,选择RAID 操作
21.  RAID actions


# 输入32,选择变更RAID设置
RAID actions menu, select an option:  [1-99 or e/p/w or 0 to quit] 32

Volume 0 is DevHandle 011d, Bus 1 Target 1, Type RAID1 (Mirroring)
Volume 1 is DevHandle 011e, Bus 1 Target 0, Type RAID1 (Mirroring)

# 输入0,选择对应RAID盘
Volume:  [0-1 or RETURN to quit] 0

  Volume 0 Settings:  write caching enabled, auto configure hot swap enabled
Volume 0 draws from Hot Spare Pools:  0

Write caching:  [0=Disabled, 1=Enabled, 2=MemberControlled, default is 1]
# 输入1,打开Write Cache!

Go团队如何解bug[1]: 乱序执行与内存屏障

开篇前言

Go项目在发展过程中有过许许多多的bug,核心开发组在解决bug的时候有各种各样的方法,涉及的知识点都很值得学习。
我觉得这些都挺值得跟大家分享,也希望做成一个不鸽系列。

正题

Issue地址:issues/35541

开发:Cherry Zhang, Michael Knyszek

这个bug是由Go的自动构建机器linux-mipsle-mengzhuo在日常测试过程中首次发现的,
表现是某个对象里的指针指向了一个不存在的span上,导致runtime panic。

Cherry根据 经验 判断出这个span类型是goroutine用的,并且贴出了对应的对象映射(如下)

object=0xc000106300 s.base()=0xc000106000 s.limit=0xc000107f80 s.spanclass=42 s.elemsize=384 s.state=mSpanInUse
*(object+0) = 0xc00052a000 g.stack.lo
*(object+8) = 0xc000532000 g.stack.hi
*(object+16) = 0xc00052a380 g.stackguard0 = g.stack.lo + _Stackguard = g.stack.lo + 896
*(object+24) = 0xffffffffffffffff g.stackguard1
*(object+32) = 0x0 g._panic
*(object+40) = 0xc0005313e0 <== // 故障点 g._defer
*(object+48) = 0xc000080380 g.m
*(object+56) = 0xc0005310b8 g.sched.sp
*(object+64) = 0x87038 g.sched.pc
*(object+72) = 0xc000106300 g.sched.g

虽然Cherry没有明说如何判断出是goroutine,但其中的偏移量24字节处的数据正好是64位的满位,
看起来确实像一般的goroutine的stackguard1 (malg时分配的)

这个bug奇怪的地方在于不是必现,是在不同的函数,时不时runtime panic, 而且出问题的span有时是在用的,有时又是废弃的。
更奇怪的是,RTRK(塞尔维亚的一家科研机构)提供的MIPS构建机从来都没有出现过一次类似原因的runtime panic。

一开始,开发组认为是MIPS的问题,时间长了,发现还有plan9-arm,
darwin-arm64-corellium这两个架构也出现了类似问题,然而出现的频率远不如MIPS高。
因为没有有效解,几乎每周Bryan Mills(负责Go开发质量的工程师)都会在Github Issue中填上新增的panic日志地址,最后他也嫌烦了……

脑袋疼.jpg

问题定位

事情的转机是我有一次debug过程中,发现runtime有个测试函数TestDeferHeapAndStack 总是 会引发这个panic。
而在Go项目的自动化整体测试中,为了测试尽快完成,通常速度慢的机器都会选择short mode,
而恰好这个short mode不会导致panic。

本身这个测试也很有意思,因为编译器只会把函数作用域中无指针的defer分配在stack上,而循环体中的defer就会分配在heap中。

测试用例如下:

func deferHeapAndStack(n int) (r int) {
if n == 0 {
return 0
}
if n%2 == 0 {
// heap-allocated defers
for i := 0; i < 2; i++ {
defer func() {
r++
}()
}
} else {
// stack-allocated defers
defer func() {
r++
}()
defer func() {
r++
}()
}
r = deferHeapAndStack(n - 1)
escapeMe(new([1024]byte)) // force some GCs
return
}

我尝试着自己修复这个bug,然而失败了,不过既然可以稳定重现,那可以帮助核心团队来bisect这个bug。

这个技能是从Austin(Go runtime leader)学来的(他如何从Go定位Linux bug正好可以写下一期哈)。

因为这个bug是从1.13之后才有的,所以我选择了HEAD 和 go1.13.9之间开始定位。

$ git bisect HEAD go1.13.9
$ git bisect run ./test.sh // 就是跑上面deferTest的脚本

最后发现是runtime: rearrange mheap_.alloc* into allocSpan这个commit导致了bug。

修复方法

Cherry在和Michael聊这个bug之后发现在allocSpan的过程中由于缺了一个内存屏障(memory barrier),
在弱内存顺序模型的机器上这个bug会随机出现。以下是她的修复commit:

When copying a stack, we
1. allocate a new stack,
2. adjust pointers pointing to the old stack to pointing to the
new stack.
If the GC is running on another thread concurrently, on a machine
with weak memory model, the GC could observe the adjusted pointer
(e.g. through gp._defer which could be a special heap-to-stack
pointer), but not observe the publish of the new stack span. In
this case, the GC will see the adjusted pointer pointing to an
unallocated span, and throw. Fixing this by adding a publication
barrier between the allocation of the span and adjusting pointers.
One testcase for this is TestDeferHeapAndStack in long mode. It
fails reliably on linux-mips64le-mengzhuo builder without the fix,
and passes reliably after the fix.

简单翻译一下:

当拷贝一个栈数据(stack)时,我们先:
1. 申请一个新的栈空间
2. 将老的栈上的指针(pointer)从老栈指向新栈上。
如果此时在另一个线程并行地执行着GC(垃圾回收),且架构是弱内存顺序模型时,GC可以观察到指针的调整
(例如: gp._defer 就是一个特殊的 heap-to-stack 指针),但是观察不到新的栈分配。
这时GC就会观察到指针分配到了一个没有分配的空间中。
我们通过在分配和调整指针之间添加内存屏障(memory barrier)来修复这个bug。

代码就更简单了,只是在allocSpan的过程中添加了一个函数publicationBarrier()

什么是内存顺序模型可以参考这个系列的文章《Memory Barriers Are Like Source Control Operations》

X86没有问题,是因为强内存模型保证了每个写入操作的顺序能在各个CPU之间保证同步。
而弱内存模型就没有保证这点,所以强制sync

等等,你问为啥ARM没有这个问题?这是玄学(划掉
因为ARM虽然是弱内存模型,但是保证了“Data dependency barrires

所以以后碰到诡异的多线程问题,就先sync(手动狗头

那么什么是内存顺序模型?

咕咕咕……回头再补

多线程问题

MESI

小结

参考资料

[^1] https://blog.golang.org/ismmkeynote
[^2] https://www.ardanlabs.com/blog/2018/12/garbage-collection-in-go-part1-semantics.html
[^3] https://zhuanlan.zhihu.com/p/48157076

龙芯 & Golang!

龙芯,不少人都比较陌生,见过的就更少了。

龙芯活着,还在云时代的2019年拯救了一下MIPS这棵34年的枯树。

一点背景故事

事情还要从去18年底,Go的MIPS架构的构建机(builder)集体下线说起。
这里顺便说一下builder的功能,其实就是Go在验证各个平台兼容性用的机器,
主要是各大公司和志愿者捐赠的,绝大部分是国内开发者很少见的PowerPC、ARM、S390X这类ISA,
操作系统更多,具体可以看看build.golang.org

19年4月时,
Go核心团队的Bradfitz发现ImgTec负责的mips、
mipsle、mips64le、mips64(大小端/32/64位)四种机型的builder已经下线半年多了,
根据Go的平台支持条件要求,任何一个架构+操作系统组合都需要有验证机型,否则就要踢出Go的支持列表。
所以Bradfitz发邮件给ImgTec维护者,
收到的只有查无此人的自动回复
他觉得是这哥们离职的原因,
但实际上是2017年底的时候MIPS背后的ImgTec把MIPS卖了……这些builder竟然还多撑了一年。

大概同时,我从国内Go开发大牛Ben那里获得了一台龙芯3A1500,
这台机器是龙芯团队希望能有人维护Go MIPS,毕竟Go已经是云时代的C了,
不少服务是运行在Go的runtime上的,
另一方面docker已经成了事实标准,龙芯云也是基于docker的。
所以把机器寄给了Ben,但Ben忙于工作,我又喜欢多管闲事性能优化……于是我愉快地收下了这台3A1500。

Loongson 3A1500

不过这台机器可能因为暴力的快递摔坏了,一直点不亮,我只好退给了Ben,
从龙梦公司通过古老的转账汇款方式买了一台3A3000。

就在我搜索MIPS可优化点的时候,发现了MIPS要被踢出去的帖子,
所以我回帖说可以让我的这台龙芯替代ImgTec做builder。
经过自己

  1. 编译4.19内核
  2. 申请密钥
  3. 改Go build项目代码
  4. 艰难地设置网络之后

龙芯的builder: linux-mipsle-mengzhuo终于上线了。(名字不是我挑的)
具体的申请builder的wiki可以看这里

龙芯Go现状

毕竟3A3000是16年的CPU,加上是1.5Ghz/8KB L3 Cache/28nm 制程自然也不能和Intel、AMD比。

其他问题嘛……

Unalign access penalty,没有Hyper Threading,SIMD支持也几乎没有。
就算这么多缺陷,龙芯也是目前市面上零售方式能买到的唯一的MIPS架构的CPU了,
MIPS新东家Wave computing是搞AI的,不知道买MIPS来干嘛,架构不发展,只是
开源了r6架构,但是看官网制程还是28nm的……
所以可以说龙芯是MIPS,这个1985年就出现的架构最后脸面了(欢迎打脸)。

大家可以看看龙芯的cpuinfo哈

Linux ls-3a3k 4.19.53+ #1 SMP PREEMPT Wed Jul 10 15:12:52 UTC 2019 mips64 mips64 mips64 GNU/Linux
system type : generic-loongson-machine
machine : loongson,generic
processor : 0
cpu model : Loongson-3 V0.13 FPU V0.1
model name : Loongson-3A R3 (Loongson-3A3000) @ 1450MHz
CPU MHz : 1450.00
BogoMIPS : 2887.52
wait instruction : yes
microsecond timers : yes
tlb_entries : 1088
extra interrupt vector : no
hardware watchpoint : yes, count: 0, address/irw mask: []
isa : mips1 mips2 mips3 mips4 mips5 mips32r1 mips32r2 mips64r1 mips64r2
ASEs implemented : dsp dsp2 vz
shadow register sets : 1
kscratch registers : 6
package : 0
core : 0
VCED exceptions : not available
VCEI exceptions : not available

说到Go在龙芯上的实际性能,通过观察,大概比PPC64、ARM这些builder快点,
Go所有源代码编译+测试一次大概要耗时25分钟左右。

不过我发现不少性能关键路径上的代码甚至都没按一台正常的64位机器写,
而是明显的“能用”的状态,可能和Minux和Cherry移植的时候先让MIPS架构能跑起来有关。
更要命的是,Go不知道为啥,最小版本要求竟然是MIPS III(1993年发布),
想在Go上用常见的优化指令,比如count leading zero(CLZ),
conditional move (CMOV.CON),
BSWAP ( ROR DSHB )
prefech统统都不行……

不过我还是提交了一些优化的CL,平时还要忙无聊的工作,精力有限,目前只有:

未来的展望

如果我能多提交一些bytealg,syscall,SSA相关优化之后应该就能更快点,
就算没有向量优化,硬件指令集,至少总体性能也应该能提升30%左右。
国内我知道在优化的人也就Xenon一个了,如果你也有兴趣搞龙芯Go优化的欢迎联系我。

有可能的话,我也想尽可能地推动核心团队提升Go MIPS的版本,MIPS III 实在是太老了。

同时我也希望各位开发们能借着“国产化”的春风,在工作中多用国产CPU,帮助提升性能,
丰富一下生态,多影响一下上游。至少不是做个冷嘲热讽的键盘侠。顺便祈祷MIPS的新东家Wave computing
不要再搞什么幺蛾子把MIPS真的送进博物馆里了。

最后附上这台builder的样子,毕竟应该是国内第一台在Go项目里的服务器。

LS 3A3K