Friday, April 15, 2011

Compile kernel for Kurobox HG

又是寫筆記的一篇文章

記得這台 Kurobox HG 已經買了至少 五六年以上了

kernel 從早期內建的 2.4.17_mvl-sandpoint 幾年之間一直不斷的升級到現在已經 2.6.32.28
(註:要使用自己的kernel必須先將機器的 bootloader
刷成 uboot,但是刷 bootloader有一定程度的風險,
不確定的朋友請勿輕易嘗試,以免機器變成磚頭)

機器上的系統也從

Debian 3.0 (woody) -> Debian 4.0 (etch) -> Debian 5.0 (lenny) -> Debian 6.0 (squeeze)
(註: Debian 的套件管理真的做的很好, 一路這樣升級都沒出什麼大問題)

年紀大了
每次 compile 新 kernel 都還要去看網頁
Compile a uBoot Kernel

記不了那麼多, 而且本人很懶也沒設 bookmark, 所以乾脆寫編文章也方便自己參考


1. Get the kernel source

# cd /usr/src
# wget http://www.kernel.org/pub/linux/kernel/v2.6/longterm/v2.6.35/linux-2.6.35.12.tar.bz2
# tar xjvf linux-2.6.35.12.tar.bz2


2. Compile your kernel
直接拿前一版的config來用比較快,而且我是 native compile 所以 ARCH=powerpc 就省了

# cp /boot/config-2.6.32.28 .config
# make oldconfig
...
...
# make uImage && make modules && make modules_install

通常我都從外面用 ssh 連回家而且開 screen 讓, 開始 make 後就 detach 了
沒意外的話應該是不會出什麼差錯

3. Backup your old kernel and dtb file

# cp /boot/uImage /boot/uImage.bak
# cp /boot/kuroboxHG.dtb /boot/kuroboxHG.dtb.bak


前面的步驟跟一般x86上 make kernel 的步驟都一樣, 讓我老是記不起來的是關於 dtb file
這些步驟, 如下

4. Compile the dtb file

# cp kuroboxHG.dtb /tmp/
# cd /usr/src/linux/arch/powerpc/boot/dts
# dtc -I dts -O dtb -V 16 -o /tmp/kuroboxHG.dtb kuroboxHG.dts
# cp /tmp/kuroboxHG.dtb /boot/kuroboxHG.dtb


5. Install the kernel & enable modules

# cp /usr/src/linux/arch/powerpc/boot/uImage /boot/uImage
# cd /lib/modules/2.6.35.12/
# depmod


6. Reboot

# reboot


7.Troubleshooting
如果不幸開不了機的話 XD
只好進去 EM mode 把剛剛備份的舊 kernel 和 dtb file copy 回來

# cp /boot/uImage.bak /boot/uImage
# cp /boot/kuroboxHG.dtb.bak /boot/kuroboxHG.dtb


(註:dtb file 是 uboot 使用的, 但是 uboot 必須要在某個版本以上才有支援(版本忘記了)
所以如果 Kurobox HG bootloader 沒刷機成 uboot 的朋友, 是不需要做出 dtb file 的)

Tuesday, April 12, 2011

Fedora - Build rpm package from src.rpm

參考
http://fedoraproject.org/wiki/Docs/CustomKernel

以下所有命令都是在非 root 下運行

在開始之前,先確保系統已安裝下列必要的包:

yum-utils
rpmdevtools


可通過下面的命令來安裝:
su -c 'yum install yum-utils rpmdevtools'

其中 yum-utils 包提供 yumdownloader 和 yum-builddep 這兩個命令,

rpmdevtools 包提供 rpmdev-setuptree 這個命令

獲得 Source
1. 首先建立 RPM 的打包環境目錄,運行下列命令:
rpmdev-setuptree

該命令會在用戶的 HOME 目錄下建立 rpmbuild/{SOURCES,SPECS,BUILD,RPMS,SRPMS} 這5個目錄,
以及一個 .rpmmacros 文件,該文件定義了一些 rpm 打包的環境變數。

2. 下載 kernel source src.rpm package。
可以直接到 fedora 的官方源上去下載,也可以用下列命令下載:
yumdownloader --source kernel

3. 安裝編譯內核所需要的包,運行下列命令:
su -c 'yum-builddep kernel-.src.rpm'

4. 安裝 kernel source src.rpm 包,運行下列命令:
rpm -Uvh kernel-.src.rpm

準備 kernel source tree
1. 運行下列命令來建立 kernel source tree:

cd ~/rpmbuild/SPECS
rpmbuild -bp --target=`uname -m` kernel.spec

現在kernel source tree位於
~/rpmbuild/BUILD/kernel-/linux-. 目錄。

2. 如果還需要修改kernel source的話(可選),可按如下步驟操作:

cp -a ~/rpmbuild/BUILD/kernel-2.6.$ver/linux-2.6.$ver.$arch ~/rpmbuild/BUILD/kernel-2.6.$ver.orig
cp -a ~/rpmbuild/BUILD/kernel-2.6.$ver.orig ~/rpmbuild/BUILD/kernel-2.6.$ver.new
在 ~/rpmbuild/BUILD/kernel-2.6.$ver.new 中對 source 作修改,然後作成 patch:
cd ~/rpmbuild/BUILD
diff -uNrp kernel-2.6.x.orig kernel-2.6.x.new > ../SOURCES/linux-2.6-my-new-patch.patch


配置內核選項(可選)
如果需要修改內核的編譯選項的話,按如下操作:
1. 進入內核源代碼樹

cd ~/rpmbuild/BUILD/kernel-/linux-./


2. 從 configs 目錄中挑選一個基於其修改的 config 檔,把它複製成內核源代碼樹中的 .config 文件

cp configs/ .config


3. 配置內核編譯選項

make oldconfig
make menuconfig


4. 在新生成的 .config 檔的第一行添加一行指定硬體平臺的標識(uname -i 的輸出),以 # 號開頭,例如:

# i386


5. 把 .config 檔複製到 ~/rpmbuild/SOURCES/ 目錄下,並命名成 config-,例如 config-i686

cp .config ~/rpmbuild/SOURCES/config-


準備 spec 文件
進入 ~/rpmbuild/SPECS 目錄,編輯 kernel.spec 文件:
1. 定義 buildid 為一個唯一的標識,以區別不同的 kernel rpm 包。把
#% define buildid .local
改成
%define buildid .
注意去掉 % 和 define 之間的空格

2. 如果之前有生成 patch ,把 patch 加到 kernel.spec 文件中。一般放在所有已有的 patch 最後,並注明注釋。例如:

# cputime accounting is broken, revert to 2.6.22 version
Patch2220: linux-2.6-cputime-fix-accounting.patch
Patch9999: linux-2.6-samfw-test.patch

另外在打 patch 那一段也要補上:

ApplyPatch linux-2.6-cputime-fix-accounting.patch
ApplyPatch linux-2.6-samfw-test.patch

編譯新的內核
用 rpmbuild 命令來編譯 kernel rpm 包,默認的命令為:
rpmbuild -bb --target=`uname -m` kernel.spec
這個命令可能會編譯好幾遍 kernel,生成不同類型的 kernel image,
例如支援 xen、smp 等的 kernel。

可以通過添加 --without

Friday, April 01, 2011

load storage adapter driver 遇到的問題

今天成功的將 CentOS 5.5 機器的 kernel更新成了 Fedora Core 14 x86 64 bits 的 kernel,

因為CentOS 5.5 的 kernel實在太老舊了,

不過在開機過程 load SAS adapter driver, 發現 driver load 完, init 仍然繼續往前執行,

如果這時有設定 LVM/MD之類的東西就會發生錯誤,

後來拜Google大神找到一個類似的問題
rc.sysinit does not wait for udev loaded scsi adapters to finish scanning their busses

應該跟 rc.sysinit 有關係, 因為筆者使用 Fedora Core 14時並沒有這個現象,
索性就直接去比較 CentOS 5.5 和 Fedora Core 14 rc.sysinit 的差別
結果發現了, Fedora Core 14 裡面有一段

# Sync waiting for storage.
{ rmmod scsi_wait_scan ; modprobe scsi_wait_scan ; rmmod scsi_wait_scan ; } >/dev/null 2>&1

加到 CentOS 5.5 rc.sysinit 後, 一切 OK, 果然如 module name scsi_wait_scan 一樣
至於為什麼要先 rmmod -> modprobe -> rmmod 就再研究了

SCSI adapter driver initial 完會call scsi_scan_host 來 scan device
為了 async scan device 會 create 一個名為 "scsi_scan_x" 的 kernel thread 來執行 do_scan_async
詳細看 void scsi_scan_host(struct Scsi_Host *shost) source code

do_scan_async() 呼叫 do_scsi_scan_host() 開始啟動 scan,
呼叫 scsi_finish_async_scan() 來通知系統 scan 完成

從source code 可以得知如果是 async scan的話, scan到一個lun的時候並不會馬上註冊device name
device name的註冊是在scsi_finish_async_scan()裡面完成的

基本上這次遇到的問題是 async scan 造成的...

scsi_wait_scan module source code 很短, 主要應該是 scsi_complete_async_scans(void)
基本上作法就是 create 一個假的 "async_scan_data" insert 到
scanning_hosts list 的尾巴, 然後就等 wait_for_completion(&data->prev_finished);

int scsi_complete_async_scans(void)
{
struct async_scan_data *data;
...
...
data = kmalloc(sizeof(*data), GFP_KERNEL);
...
init_completion(&data->prev_finished);
...
...
list_add_tail(&data->list, &scanning_hosts);
...
printk(KERN_INFO "scsi: waiting for bus probes to complete ...\n");
wait_for_completion(&data->prev_finished);
...
return 0;
}


如果沒理解錯誤的話, 應該是會在
void scsi_finish_async_scan(struct async_scan_data *data) 中
被 complete(&next->prev_finished);

也因此 scsi_wait_scan module return 時只保證在它之前就啟動的scan

已經結束, 若是在它之後才啟動的scan 則有可能已經結束或未結束