読者です 読者をやめる 読者になる 読者になる

nigakyのブログ

雑多なメモです。

Linux kernel 3.9 の新機能 SO_REUSEPORT を試してみる

Linux kernel 3.9 のマージウィンドウでは SO_REUSEPORT というソケットオプションがマージされました。
Merge branch 'soreuseport' · c617f39 · torvalds/linux · GitHub
これは同一ポートに複数のリスナー(listen ソケット)が bind できるようになるというもので、Webサーバなど単一のポートに多くのコネクションが来るようなワークロードで、複数プロセスでうまく負荷分散ができるようになるそうです。
(これまでだと一人がaccept()してそれぞれの worker に渡すというようなモデルがありましたが、これでは accept() する部分がボトルネックになってしまいがちでした。)

BSDでは元々 SO_REUSEPORT オプションはあったようですが、マルチキャスト通信で使うもののようです。Linux のこの実装は目的が違うので少し混乱を招きそうですが…

まずは面白そうな機能なので実際に試してみました。

準備

カーネルは最新のものをgitで取ってきました。

# uname -r
3.9.0-rc2+

お試しプログラム

お手軽に試すため Python で書きました。
10プロセスが同一ポートに bind して、誰が accept したかを表示するものです。

#!/usr/bin/env python

import sys, socket, time
from multiprocessing import Process

PORT = 8000
NR_LISTENERS = 10

SO_REUSEPORT = 15

def listener_work(num):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.setsockopt(socket.SOL_SOCKET, SO_REUSEPORT, 1)   # set SO_REUSEPORT
    s.bind(("", PORT))
    s.listen(5)

    while True:
        conn, addr = s.accept()
        print '%2d: accepted!' % num
        ret = conn.recv(16)
        conn.close()

def server():
    processes = []
    for i in xrange(NR_LISTENERS):
        p = Process(target=listener_work, args=(i,))
        p.start()
        processes.append(p)

    for p in processes:
        p.join()

def client():
    while True:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((sys.argv[1], PORT))
        s.sendall("a" * 16)
        s.close()
        time.sleep(1)

def main():
    if '-s' in sys.argv:
        server()
    else:
        client()


if __name__ == '__main__':
    main()

実行結果

クライアント側

# ./reuseport.py <サーバのIP>

サーバ側

# ./reuseport.py -s
 0: accepted!
 2: accepted!
 3: accepted!
 4: accepted!
 0: accepted!
 6: accepted!
 1: accepted!
 3: accepted!
 9: accepted!
 5: accepted!
     :

SO_REUSEPORT を使うことで、10プロセスが同一ポートに bind できました。
また、accept する人も少しムラはありますがそこそこうまく分散されているようです。

実際に効果があるかを見るにはもっと高負荷でないと分からないですが、
まずは動くことが確認できました。
最近は TCP のプロトコルを改良するようなパッチが色々とマージされていて面白いですね。

TCP Fast Open を試してみる

Linux カーネル 3.6 では TCP Fast Open (TFO) という機能がマージされたそうです。詳しくは以下の URL に記載がありますが、一度接続したクライアントは TCP の 3way-handshake を簡略化してコネクションをオープンできる(SYN にデータを載せられる)という機能のようです。

TCP Fast Open: expediting web services [LWN.net]

面白そうな機能なので実際に使ってみて、使い方をまとめてみました。

準備

カーネル

kernel 3.6 ではまだクライアント側の機能しかマージされていないので、さらに新しいカーネルを使います(3.7 でサーバ側の機能もマージされるようです)。今回は git で取ってきた最新 (12250d843e8489ee00b5b7726da855e51694e792) をビルドして使いました。

TFO の有効化

TFO はデフォルトでオフになっているため、以下のパラメータで有効化します。

 # echo 3 > /proc/sys/net/ipv4/tcp_fast_open

TFO を利用したソケットプログラム

TFO を使うにはサーバ側、クライアント側それぞれで配慮が必要です。

具体的には

  • サーバ側はソケットオプション TCP_FASTOPEN (23) をセットする
  • クライアント側は connect(2)/send(2) の代わりに MSG_FASTOPEN (0x20000000) フラグをつけた sendto(2) を使う

以下の様なテストプログラムを使いました。
クライアントは(TFO Cookies をもらうため)一度通常のコネクションで通信し、二回目に TFO を使って通信しています。

#!/usr/bin/env python

import sys, socket

PORT = 8000

TCP_FASTOPEN = 23
MSG_FASTOPEN = 0x20000000

def server():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.setsockopt(socket.SOL_TCP, TCP_FASTOPEN, 5) # set TCP_FASTOPEN option
    s.bind(("", PORT))
    s.listen(5)

    while True:
        conn, addr = s.accept()
        ret = conn.recv(1024)
        while len(ret) > 0:
            print ret
            ret = conn.recv(1024)
        conn.close()
    s.close()

def client():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((sys.argv[1], PORT))
    s.sendall("1. First connection.")
    s.close()

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # sendto with MSG_FASTOPEN flag
    s.sendto("2. Sending data by using TCP fast open!!!", MSG_FASTOPEN, (sys.argv[1], PORT))
    s.close()

def main():
    if '-s' in sys.argv:
        server()
    else:
        client()


if __name__ == '__main__':
    main()

試してみる

今回はサーバ/クライアントを一台のループバックでやっています。

サーバ側

# ./TFO_test.py -s
1. First connection.
2. Sending data by using TCP fast open!!!

クライアント側

# ./TFO_test.py localhost

これだけ見てもよく分からないので tcpdump も見てみます。
(TFO を使ってコネクションを確立している部分のダンプです。)

ポイントは

  • クライアントは SYN パケットに TFO Cookies とデータを載せて送信している
  • SYN パケットにデータが載っているため、今回のテストプログラムでは 3way-handshake 終了後に即 FIN が送られてソケットがクローズされている(データを別で送らない分パケットのやり取りが通常のコネクションより一回少ない。)
IP localhost.42887 > localhost.irdmi: Flags [S], seq 914856703:914856744, win 43690, options [mss 65495,sackOK,TS val 182325 ecr 0,nop,wscale 6,Unknown Option 254f9896c02881aefd4e7c0], length 41
IP localhost.irdmi > localhost.42887: Flags [S.], seq 2336617123, ack 914856745, win 43690, options [mss 65495,sackOK,TS val 182325 ecr 182325,nop,wscale 6], length 0
IP localhost.42887 > localhost.irdmi: Flags [.], ack 1, win 683, options [nop,nop,TS val 182325 ecr 182325], length 0
IP localhost.42887 > localhost.irdmi: Flags [F.], seq 1, ack 1, win 683, options [nop,nop,TS val 182326 ecr 182325], length 0
IP localhost.irdmi > localhost.42887: Flags [F.], seq 1, ack 2, win 683, options [nop,nop,TS val 182326 ecr 182325], length 0
IP localhost.42887 > localhost.irdmi: Flags [.], ack 2, win 683, options [nop,nop,TS val 182326 ecr 182326], length 0

たぶんこれで TFO が使えていると思います。
netstat で見ると以下のカウンタも上がっていました。

# netstat -s | grep TCPFastOpen
    TCPFastOpenActive: 7
    TCPFastOpenPassive: 9
    TCPFastOpenCookieReqd: 2

これが Web で使われるようになれば応答性が良くなって幸せになれそうですね。

scapy でソケット通信

最近の高級な言語を使ったネットワークプログラミングではソケットすら意識しなくてもプログラムができてしまいますが、実際にどんな仕組みで通信されているかを知ることは重要です。

そこで scapy という任意のパケットを生成するソフトを使ってパケットを一つ一つ作成し、echo サーバとTCP で通信してみたいと思います。

Scapy

scapy のインストール

scapy は Ubuntu ではパッケージになっているので apt で入りますが、CentOS6 ではパッケージになっていなかったので自分で取ってきて入れました。

# wget http://www.secdev.org/projects/scapy/files/scapy-latest.tar.gz
# tar xvf scapy-latest.tar.gz
# cd scapy-2.1.0
# ./setup.py install

scapy の簡単な使い方

scapy は python のモジュールですが、インストールすると専用コマンドも一緒にインストールされます。

# scapy
Welcome to Scapy (2.1.0)
>>>

試しに www.google.com 宛の ICMP Echo リクエスト (ping) のパケットを作ってみます。

>>> ping=IP(dst='www.google.com')/ICMP()
>>> ping.show()
###[ IP ]###
  version= 4
  ihl= None
  tos= 0x0
  len= None
  id= 1
  flags=
  frag= 0
  ttl= 64
  proto= icmp
  chksum= None
  src= 192.168.11.76
  dst= Net('www.google.com')
  \options\
###[ ICMP ]###
     type= echo-request
     code= 0
     chksum= None
     id= 0x0
     seq= 0x0

IP() や ICMP() でそれぞれのレイヤを作成でき、それを "/" で繋ぐとパケットになります。
パケットの中身は show() で見ることができます。
パラメータで指定しなかった chksum や ttl などの部分はうまく作ってくれるので非常に楽です。
それではこの ping パケットを実際に送信してみます。

>>> reply = sr1(ping)
Begin emission:
.......................Finished to send 1 packets.
...............................*
Received 55 packets, got 1 answers, remaining 0 packets
<IP  version=4L ihl=5L tos=0x0 len=28 id=51106 flags= frag=0L ttl=44 proto=icmp chksum=0x2f8 src=64.233.183.105 dst=192.168.11.7>
>>> reply.show()
###[ IP ]###
  version= 4L
  ihl= 5L
  tos= 0x0
  len= 28
  id= 51106
  flags=
  frag= 0L
  ttl= 44
  proto= icmp
  chksum= 0x2f8
  src= 64.233.183.105
  dst= 192.168.11.76
  \options\
###[ ICMP ]###
     type= echo-reply
     code= 0
     chksum= 0x0
     id= 0x0
     seq= 0x0

sr1() はパケットを送信し、戻ってきたパケットを返すメソッドです。
返ってきたパケットを見ると、確かに echo-reply が戻ってきていることがわかります。

echo サーバと TCP 通信

今回は echo サーバとソケットをオープンしてデータを送信し、ソケットをクローズするところまでやってみます。
TCP の状態遷移図は以下のページに載っています。

@IT:連載 基礎から学ぶWindowsネットワーク 第16回 信頼性のある通信を実現するTCPプロトコル(3) 2.TCPの状態遷移図

サーバ側の準備

サーバ側は netcat を使ってポート 30000 を開いて待っておきます。

# nc -l 30000

このサーバに対して、scapy を使ってパケットを送りつけることでソケット通信を行います。
今回の環境ではサーバの IP は 192.168.11.1 です。

クライアント側の準備

実際にパケットを送る前にまずは以下のコマンドで、RST パケットの送信を抑止しておきます。
クライアント側は実際にはソケットがないため、サーバからのリプライに対して OS が RST を送ってしまうためです。

# iptables -A OUTPUT -p tcp --tcp-flags RST RST -d <サーバのIP>  -j DROP

今回の環境ではクライアント側の IP は 192.168.11.76 です。

ソケットのオープン

まずは 3way ハンドシェイクでセッションを確立します。
SYN を送って返ってきた SYN+ACK に対して ACK を送ります。

>>> ip=IP(dst='192.168.11.1')
>>> tcp=TCP(sport=50000,dport=30000,seq=100)

dst にはサーバの IP を指定し、dport には nc で待ち受けているポートを指定します。
sport, seq は適当です。

>>> tcp.flags = 'S'
>>> SYN=ip/tcp
>>> SYNACK=sr1(SYN)
Begin emission:
..............................Finished to send 1 packets.
...*
Received 34 packets, got 1 answers, remaining 0 packets

SYN フラグを立てたパケットを送信して SYN+ACK を受け取ります。
この時点でのサーバの状態は TCP 状態遷移図でいうところの SYN_RECV です。
(サーバで netstat をすると状態が確認できます。)

>>> tcp.seq += 1
>>> tcp.ack = SYNACK.seq + 1
>>> tcp.flags = 'A'
>>> send(ip/tcp)
.
Sent 1 packets.

返ってきた SYN+ACK に対して ACK を返します。
これでサーバの状態は ESTABLISHED になり、セッションが確立できました。

データ送信

セッションができたので実際のデータを送信します。

>>> payload="Hello world!"
>>> DATA=ip/tcp/payload
>>> ACK=sr1(DATA)
Begin emission:
...............................Finished to send 1 packets.
..*
Received 34 packets, got 1 answers, remaining 0 packets
>>> tcp.seq += len(payload)

サーバの nc を実行したプロンプトを見てみると、"Hello world!" が表示されているはずです。
送信したデータのサイズ分シーケンス番号を進めておきます。

ソケットのクローズ

ソケットをクローズするために FIN フラグを立てたパケットを送ります。

>>> tcp.flags = 'FA'
>>> FINACK=sr1(ip/tcp)
Begin emission:
............................Finished to send 1 packets.
..*
Received 31 packets, got 1 answers, remaining 0 packets
>>> FINACK.show()
###[ IP ]###
  version= 4L
  ihl= 5L
  tos= 0x0
  len= 40
  id= 65164
  flags= DF
  frag= 0L
  ttl= 64
  proto= tcp
  chksum= 0xa4a5
  src= 192.168.11.1
  dst= 192.168.11.76
  \options\
###[ TCP ]###
     sport= 30000
     dport= 50000
     seq= 788872184
     ack= 114
     dataofs= 5L
     reserved= 0L
     flags= FA
     window= 5840
     chksum= 0x5d75
     urgptr= 0
     options= {}

サーバは何も送信するデータがないため、即時 FIN を返してきます。
サーバの状態は LAST_ACK です。

>>> tcp.seq += 1
>>> tcp.ack = FINACK.seq + 1
>>> tcp.flags = 'A'
>>> send(ip/tcp)

最後に ACK を返してやることでソケットは正常にクローズされます。

これでひと通りの TCP 通信ができました。
scapy を使うと色々なことができそうなのでもっと使ってみようと思います。

Ubuntu を BeyondRAID 環境へ移行する (2/2)

ファイルサーバデータの移行

OS 領域と同様に、以下のような流れで行います。
BeyondRAID 環境を構築します。

  1. MDRAID 構築
  2. LVM、ファイルシステム構築
  3. 既存データをコピー
  4. 既存ディスクの RAID 組み込み
MDRAID 構築

3つのディスクを使う部分は RAID5、2つのディスクを使う部分は RAID1 にします。
データ移行のため、まずは2つのディスクを使って RAID5、1つのディスクで RAID1 を構成します。

# mdadm -C -l5 -n3 -e1 --assume-clean /dev/md2 /dev/sdb4 /dev/sdc5 missing
# mdadm -C -l1 -n2 -e1 --assume-clean /dev/md3 /dev/sdb5 missing
LVM、ファイルシステム構築

2つの md デバイスをまとめて一つの LVM ボリュームにします。

# pvcreate /dev/md2
# pvcreate /dev/md3
# vgcreate BeyondRAIDVG0 /dev/md2 /dev/md3
# lvcreate -n BeyondRAIDLV0 -l 324317 BeyondRAIDVG0  ☆PEの数 (324317) は vgdisplay で確認出来ます
# mkfs -t ext4 /dev/mapper/BeyondRAIDVG0-BeyondRAIDLV0
既存データをコピー

rsync でコピーします。
800GB ほどデータがあったので一晩かかりました。

# mount /dev/mapper/BeyondRAIDVG0-BeyondRAIDLV0 /mnt
# rsync -aSv /opt/oldfiles /mnt/
# vim /etc/fstab  ☆新しい領域を使うように fstab を編集
既存ディスクの RAID 組み込み

これで既存ディスク (sda) のデータはすべて新しいディスクに移行できたので、パーティションを切りなおして RAID に組み込みます。

# parted -s /dev/sda p
モデル: ATA Hitachi HDT72101 (scsi)
ディスク /dev/sda: 1000GB
セクタサイズ (論理/物理): 512B/512B
パーティションテーブル: msdos

番号  開始    終了    サイズ  タイプ    ファイルシステム  フラグ
 1    32.3kB  510MB   510MB   primary   ext3              boot
 2    511MB   1512MB  1001MB  primary
 3    1512MB  20.0GB  18.5GB  primary                     raid
 4    20.0GB  1000GB  980GB   extended                    lba
 5    20.0GB  400GB   380GB   logical                     raid
 6    400GB   1000GB  600GB   logical                     raid

# mdadm --add /dev/md1 /dev/sda4
# mdadm --add /dev/md2 /dev/sda5
# mdadm --add /dev/md3 /dev/sda6

RAID1 への write 性能は遅いディスクに引っ張られる(と思う)ので、1TB(sda) と 2TB(sdb) のディスクで RAID1 を構成するように変更します。

# mdadm --fail /dev/md1 /dev/sdc4    ☆sdc4 を外すと自動的に sda4 が RAID1 に組み込まれる
# mdadm --remove /dev/md1 /dev/sdc4
# mdadm --add /dev/md1 /dev/sdc4     ☆sdc4 はスペアに

最終構成

余っていた 1TB のパーティションも RAID なしの LVM ボリュームにしました。

# cat /proc/mdstat
md4 : active raid1 sdb6[0]
      976753528 blocks super 1.0 [2/1] [U_]

md3 : active raid1 sda6[2] sdb5[0]
      586048600 blocks super 1.0 [2/2] [UU]

md2 : active raid5 sda5[3] sdc5[1] sdb4[0]
      742358656 blocks super 1.0 level 5, 64k chunk, algorithm 2 [3/3] [UUU]

md0 : active raid1 sdb1[2] sdc1[0]
      498676 blocks super 1.0 [2/2] [UU]

md1 : active raid1 sdc3[0](S) sdb3[2] sda3[3]
      18054072 blocks super 1.0 [2/2] [UU]

unused devices: <none>

# lvs -o +devices
  LV            VG            Attr   LSize   Origin Snap%  Move Log Copy%  Convert Devices
  BeyondRAIDLV0 BeyondRAIDVG0 -wi-ao   1.24t                                       /dev/md2(0)
  BeyondRAIDLV0 BeyondRAIDVG0 -wi-ao   1.24t                                       /dev/md3(0)
  TMPLV0        NoRAIDVG0     -wi-a- 931.50g                                       /dev/md4(0)
  Ubuntu10.04   OSImageVG0    -wi-ao  17.21g                                       /dev/md1(0)

# df -h
ファイルシステム            サイズ  使用 残り 使用% マウント位置
/dev/mapper/OSImageVG0-Ubuntu10.04
                       17G  4.9G   12G  30% /
none                  1.7G  364K  1.7G   1% /dev
none                  1.7G  1.4M  1.7G   1% /dev/shm
none                  1.7G  488K  1.7G   1% /var/run
none                  1.7G     0  1.7G   0% /var/lock
none                  1.7G     0  1.7G   0% /lib/init/rw
/dev/sda1             471M   55M  392M  13% /boot
/dev/mapper/BeyondRAIDVG0-BeyondRAIDLV0
                      1.3T  715G  469G  61% /opt/share
/dev/mapper/NoRAIDVG0-TMPLV0
                      917G  200M  871G   1% /opt/tmplv

Ubuntu を BeyondRAID 環境へ移行する (1/2)

最近はディスクも安くなってきたので、既存の1ディスクで運用しているファイルサーバ (Ubuntu) を RAID 環境に移行してみたいと思います。

複数の容量の異なるディスクで冗長性をもたせるには BeyondRAID というやりかたがおもしろそうです。すでに同じことをされている方もいますが、既存の環境を壊さずに移行するというところにこだわってやってみたいと思います。
nakanote blog 
UbuntuでDrobo風なNASを作る

環境

移行前

1TB のディスク1つ、RAID なし。

  • sda(1TB) : OS領域(20GB), スワップ(2GB), ファイル置き場(978GB)
移行後
  • OS領域は RAID1、ファイル置き場は BeyondRAID で冗長性と拡張性を持たせる
  • 400GB, 1TB, 2TB のディスク3つで冗長性を持たせた環境を構築
  • ファイルシステムext4 に移行
Drives
|  sdc   |  sda   |  sdb   |
| 400 GB |  1 TB  |  2 TB  |
                   ---------
                  |  sdb6  |       (1TB)                 -> TMP領域(RAID なし)
                   ---------
         -------------------
         |  sda5  |  sdb5  | RAID1 (2 x 600GB)           -> md3 (600GB)
         -------------------
----------------------------
| sdc4   | sda4   | sdb4   | RAID5 (3 x 378GB)           -> md2 (756GB)
----------------------------
----------------------------
| sdc3   | sda3   | sdb3   | RAID1 (2 x 20GB + 1 spare)  -> /: md1 (20GB)
----------------------------
----------------------------
| sdc2   | sda2   | sdb2   | Swap  (3 x 1GB)             -> 個別にマウント (3GB)
----------------------------
----------------------------
| sdc1   | sda1   | sdb1   |       (3 x 512MB)           -> /boot: (512MB)
----------------------------

OS 領域の移行

まずは sda に入っていた OS 領域を RAID1 環境に移行します。
以下のような流れで行います。

  1. パーティション設定
  2. MDRAID 構築
  3. LVM、ファイルシステム構築
  4. 既存 OS 領域をコピー
  5. 新しい OS 領域から起動
パーティション設定

4k セクタ (AFT) 対応に注意しながらパーティションを切ります。
最近の parted はアラインされていないと警告が出るようになっていました。

  • 2TB ディスクは 4k セクタ (AFT) なのでパーティションのアラインメントに注意する
  • 開始は 2048 セクタから(Win7 標準?)
  • 2TB ディスクは GPT で(LinuxMBR でも GPT でもどちらでもブートできるようです)
sdc (400GB) のパーティション設定

# parted /dev/sdc
(parted) mklabel msdos
(parted) mkpart primary 2048s 512M
(parted) mkpart primary 512M 1512M
(parted) mkpart primary 1512M 20G
(parted) mkpart extended 20G -1
(parted) mkpart logical 20G -1
(parted) set 1 raid on
(parted) set 3 raid on
(parted) set 5 raid on

(parted) print
モデル: ATA ST3400820AS (scsi)
ディスク /dev/sdc: 400GB
セクタサイズ (論理/物理): 512B/512B
パーティションテーブル: msdos

番号  開始    終了    サイズ  タイプ    ファイルシステム  フラグ
 1    1049kB  512MB   511MB   primary   ext3              raid
 2    512MB   1512MB  1000MB  primary   linux-swap(v1)
 3    1512MB  20.0GB  18.5GB  primary                     raid
 4    20.0GB  400GB   380GB   extended                    lba
 5    20.0GB  400GB   380GB   logical                     raid
sdb (2TB) のパーティション設定
# parted /dev/sdb
(parted) mklabel gpt
(parted) mkpart /boot 2048s 512M
(parted) mkpart swap 512M 1512M
(parted) mkpart OSImageVG0 1512M 20G
(parted) mkpart BeyondRAID0 39063552s 781422591s   ☆小さいディスクにセクタ単位で合わせます
(parted) mkpart BeyondRAID1 781422592s 1953520063s ☆小さいディスクにセクタ単位で合わせます
(parted) set 1 raid on
(parted) set 3 raid on
(parted) set 4 raid on
(parted) set 5 raid on

(parted) print
モデル: ATA WDC WD20EARS-00M (scsi)
ディスク /dev/sdb: 2000GB
セクタサイズ (論理/物理): 512B/512B
パーティションテーブル: gpt

番号  開始    終了    サイズ  ファイルシステム  名前         フラグ
 1    1049kB  512MB   511MB   ext3              /boot        raid
 2    512MB   1512MB  1000MB                    swap
 3    1512MB  20.0GB  18.5GB                    OSImageVG0   raid
 4    20.0GB  400GB   380GB                     BeyondRAID0  raid
 5    400GB   1000GB  600GB                     BeyondRAID1  raid
MDRAID 構築

OS 領域は RAID1 で構築します。
/boot も RAID1 にするつもりでしたが、grub2 のバグ?でできなかったので、ブートに利用する sda1 は単体にしました。
(sdb1 と sdc1 で RAID1 にして、手動で sda1 のバックアップを取ることにしました。)
Bug #527401 “grub-installer fails to install on a raid1 array” : Bugs : “grub2” package : Ubuntu

# mdadm -C -l1 -n2 -e1  --assume-clean /dev/md0 /dev/sdb1 /dev/sdc1
# mdadm -C -l1 -n2 -e1  --assume-clean /dev/md1 /dev/sdb3 /dev/sdc3
# mdadm --detail --scan >> /etc/mdadm/mdadm.conf

# cat /proc/mdstat
Personalities : [linear] [multipath] [raid0] [raid1] [raid6] [raid5] [raid4] [raid10]
md1 : active raid1 sdb3[2] sdc3[0]
      18054072 blocks super 1.0 [2/2] [UU]

md0 : active raid1 sdb1[2] sdc1[0]
      498676 blocks super 1.0 [2/2] [UU]

(initrd も更新しないと新しい OS 領域からブート出来なかったのでここで更新しておきます。)
# mv /boot/initrd.img-2.6.32-24-generic{,.org}
# mkinitramfs -o /boot/initrd.img-2.6.32-24-generic `uname -r`
LVM、ファイルシステム構築

MD の上に LVM を構築して ext4 でフォーマットします。

# pvcreate /dev/md1
# vgcreate OSImageVG0 /dev/md1
# lvcreate -n Ubuntu10.04 -L 17.21g OSImageVG0
# lvs -o +devices
  LV          VG         Attr   LSize  Origin Snap%  Move Log Copy%  Convert Devices
  Ubuntu10.04 OSImageVG0 -wi-ao 17.21g                                       /dev/md1(0)
# mkfs -t ext4 /dev/mapper/OSImageVG0-Ubuntu10.04
既存 OS 領域をコピー

既存の OS 領域をコピーするために CDROM などから Linux を起動します。
私は Ubuntu 10.10 Server を USB メモリブートで利用したのですが、rsync が入っていなくて面倒だったので他のものにした方がいいかもしれません。(Desktop 版では mdadm と lvm2 が入っていないので Server にしたのですが。)

rsync で OS 領域をコピー

# mount /dev/sda5 /mnt/0  ☆古い OS 領域をマウント
# mount /dev/mapper/OSImageVG0-Ubuntu10.04 /mnt/1
# rsync -av /mnt/0/ /mnt/1/ ☆OS 領域を丸ごとコピー
# vim /mnt/1/etc/fstab      ☆ fstab を新しい OS 領域 (/dev/mapper/OSImageVG0-Ubuntu10.04) に変更

新しい OS 領域からブートするため、新しい grub エントリを追加します。
/etc/grub.d/40_custom に以下を追加します。(uuid は適宜変更してください。)

menuentry 'Ubuntu, with Linux 2.6.32-27-generic' --class ubuntu --class gnu-linux --class gnu --class os {
        recordfail
        insmod ext2
        set root='(hd0,1)'
        search --no-floppy --fs-uuid --set aaaa-bbbb-cccc-dc5be58cec49
        linux   /vmlinuz-2.6.32-27-generic root=/dev/mapper/OSImageVG0-Ubuntu10.04 ro apparmor=0
        initrd  /initrd.img-2.6.32-27-generic
}

/etc/default/grub を編集して grub の選択画面が出るようにします。

#GRUB_HIDDEN_TIMEOUT=0             ☆コメントアウト
#GRUB_HIDDEN_TIMEOUT_QUIET=true    ☆コメントアウト
GRUB_HIDDEN_TIMEOUT_QUIET=false    ☆追加
GRUB_TIMEOUT=3                     ☆変更
新しい OS 領域から起動

初回のみ新しい OS 領域からブートするため、ブート時の OS 選択画面で先ほど追加したエントリを選択します。
以降自動的に新しい OS 領域からブートするようにするため、grub.cfg を更新しておきます。

# update-grub2

ルートパーティションが新しい LVM ボリュームになっていたら移行完了です。

# df
ファイルシステム           1K-ブロック    使用   使用可 使用% マウント位置
/dev/mapper/OSImageVG0-Ubuntu10.04
                      17763668   5046296  11815024  30% /
none                   1764460       344   1764116   1% /dev
none                   1769508         0   1769508   0% /dev/shm
none                   1769508       344   1769164   1% /var/run
none                   1769508         0   1769508   0% /var/lock
none                   1769508         0   1769508   0% /lib/init/rw
none                  17763668   5046296  11815024  30% /var/lib/ureadahead/debugfs
/dev/sda1               482214     56211    401104  13% /boot

IS01のカーネルをビルドしてみた

IS01のアップデートがないことが確定してしまい残念な限りです。

ところで、この IS01 は android ということもあり、カーネルが OSS として公開されているので、せっかくなのでビルドしてみました。

準備

まずは必要なものをダウンロードしてきます。
(参考:AndroidエミュレータでLinuxカーネルをデバッグ!! (1/3) − @IT MONOist

その他必要に応じて開発環境のパッケージなど。


以下のように展開します。

# mkdir android
# cd android
# tar zxvf /path/to/kernel.tar.gz
# git clone git://android.git.kernel.org/platform/prebuilt.git
# cp /path/to/config.is01 /kernel/.config

カーネルソースの修正

そのままではコンパイルエラーが出たのでソースを少し修正しました。

--- arch/arm/mach-msm/sh_sleepcheck.c.orig      2010-11-19 00:00:57.000000000 +0900
+++ arch/arm/mach-msm/sh_sleepcheck.c   2010-11-19 00:01:08.000000000 +0900
@@ -24,7 +24,7 @@
 #include <linux/wakelock.h>
 #include <linux/pm.h>
 #include <mach/msm_rpcrouter.h>
-#include <smd_private.h>
+#include "smd_private.h"
 #include <sharp/sh_sleepcheck.h>
 
 /* ==========================================================================================

コンパイル

後はコンパイルをするだけです。

# cd kernel
# make ARCH=arm CROSS_COMPILE=../prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/arm-eabi-
        :
  OBJCOPY arch/arm/boot/Image
  Kernel: arch/arm/boot/Image is ready
  Building modules, stage 2.
        :
# ls -l arch/arm/boot/Image 
-rwxr-xr-x 1 nobody nobody 5338336 2010-11-19 00:06 arch/arm/boot/Image

(たぶん)これでできました。

実行

実機にはインストールできないので、エミュレータで実行しようとしたのですがうまくできませんでした。
(出来上がった Image を -kernel オプションで指定すればいけるかと思ったのですが…)
android はあまり詳しくないのでこれ以上分からず、このあたりであきらめることにしました。

DSC-HX5V で撮った動画をDLNA を利用し PS3 で見る

Cyber-shot HX5V では AVCHD または MP4 形式で動画を撮ることができます。
撮った動画は HDMI ケーブルで繋ぐといった方法で見ることができますが、せっかくなので DLNA を利用して PS3 で見られるようにしてみました。
DLNA クライアントの機能があれば PS3 意外でも見られるはずです。)

DLNA サーバの設定

利用する DLNA サーバはMediaTomb - Free UPnP MediaServerです。
サーバは Ubuntu 10.04 (x86_64) なので、apt で入れたもの (mediatomb-0.12.0~svn2018-6ubuntu2) を使いました。

迷うところはほぼ無かったのですが、m2ts と mp4 のファイルをそのままの設定では認識しなかったので、/etc/mediatomb/config.xml を以下のように修正。

--- config.xml.old      2010-05-16 18:10:19.000000000 +0900
+++ config.xml  2010-05-16 17:58:27.000000000 +0900
@@ -82,6 +82,8 @@
         <map from="mka" to="audio/x-matroska"/>
         <!-- Uncomment the line below for PS3 divx support -->
         <map from="avi" to="video/divx"/>
+        <map from="m2ts" to="video/mpeg"/>
+        <map from="mp4" to="video/mp4"/>
         <!-- Uncomment the line below for D-Link DSM / ZyXEL DMA-1000 -->
         <!-- <map from="avi" to="video/avi"/> -->
       </extension-mimetype>
@@ -101,8 +103,8 @@
         <treat mimetype="audio/x-wav" as="pcm"/>
         <treat mimetype="audio/L16" as="pcm"/>
         <treat mimetype="video/x-msvideo" as="avi"/>
-        <treat mimetype="video/mp4" as="mp4"/>
-        <treat mimetype="audio/mp4" as="mp4"/>
+        <!-- <treat mimetype="video/mp4" as="mp4"/>
+        <treat mimetype="audio/mp4" as="mp4"/> -->
         <treat mimetype="application/x-iso9660" as="dvd"/>
         <treat mimetype="application/x-iso9660-image" as="dvd"/>
         <treat mimetype="video/x-matroska" as="mkv"/>

これで PS3 で認識する mediatomb から動画が見られました。
同じ SONY 同士ということもあってか、動画の形式などに悩ませられることもなく見ることが出来ています。

mediatomb ではディレクトリを監視するよう設定することも出来るので、あらかじめ動画を取り込むディレクトリを監視しておくことで、デジカメから動画ファイルを取り込むだけで自動的に DLNA 経由で公開され、PS3 で見ることが出来るようになります。


DLNA は便利でよいですね。