DeviceTreeの基礎

2019/03/23import

device.tree.org

devicetreeの実装サイドからの仕様を公開していると思われます。

そもそもdevicetreeとはコンピュータシステムの構成要素を記述するために用いられています。

冒頭で 関連している仕様として、以下が挙げられています:

  • IEEE 1275 Open Firmware standard―IEEE Standard for Boot (Initialization Configuration) Firmware: Core Requirements and Practices [IEEE1275].
  • EPAPR

この仕様書は、2016年からlinaro社, ARM社によって生成されている模様です。

コンピュータシステムの初期化を行う際に、OS起動前に初期化処理の必要なハードウェア類があります。ブートローダ、ハイパーバイザ等が初期化等を行って、最終的にOSへと処理を移します。このプログラム間で情報のやりとりを行うための規定にも使えるもの、とあります。

仕様の詳細は、Devicetree Specificationを参照しましょう。


なぜ必要か

PCでは ACPIがハードウェア情報を提供してくれるため、必要な設定・ドライバのロードが可能でした。

そこで Open Firmwareの頃から powerpcアーキテクチャで devicetreeの利用が始まっていました。その後 Linux kernelソースコードの汚い肥大化をLinusに指摘されたことにより、ARMアーキテクチャにも適用することとなったものです。したがって、poewrpcやARMアーキテクチャのカーネルドライバ・u-bootに関わる方には必須の事項となります。

ブートプログラムがシステム内のハードウェアの初期化処理を行うために必要な情報を得るために必要です。これがなければ、ブートプログラムはシステム内のハードウェア構成にあわせて個々に用意する必要があります。これではブートプログラムの実装効率も悪く、同じようなコードが散乱することになりかねません。そこで、ドライバソフトウェアを個々のハードウェアに対応したものとし、そのリソースを含めて定義できるようにすることでソフトウェアを共通化することができます。

その昔、PC/AT互換機が出てから `Plug and Play` が登場するまでは、ボードのリソース設定等をディップスイッチで行って、インストール作業者がリソース競合を回避するといった作業を行っていました。

PCIデバイスでは、ターゲット側がconfiguration spaceを用意することで、デバイスの素性や要求リソースを検出する仕組みが用意されています。したがって、Devitetreeはこれらのターゲットデバイスの定義を含めないこととしています(例外として、ソフト的にproveできないPCI host bridge deviceを挙げています)。


Devicetree format(概要編)

Devicetree Specificationに詳細な記述仕様があります。ざっと要点を抜粋していきます。

tree状に定義する様式となっており、'/'をルートノードとして、ファイルシステムのディレクトリ階層のように記述していきます。簡単な記述例を以下に示します。実用的な記述ではありませんが、コンパイルは通ります。

/dts-v1/;
/ {
        #address-cells = <1>;
        #size-cells = <1>;

        soc {
                compatible = "simple-bus";
                #address-cells = <1>;
                #size-cells = <1>;
                intc: intc@100 {
                        reg = <1000 100>;
                };
                spi: spi@0 {
                        reg = <0 0>;
                        interrupts = <&intc 1>;
                };
    };
};

ここで socintc@100 はノードといいます。中括弧で括った要素(プロパティ)をまとめる器です。これはバスを表現したり、特定の機能をもつIPコアを表現したり、Flash memoryやセンサICを表現したりします。

また、ノードは何段でも階層化した記述ができ、CPUから見えるシステムバスの多段表現や、その先にあるIPコアのレジスタインタフェースを表現することができます。SoC内部に限らず、SPI・I2C・非同期バスなどを介して接続するデバイスを表現します。

それぞれのノードに、プロパティを定義します。プロパティは 項目名と値とからなる組み合わせで、値がないものも存在します。値はDTspecで規定されているものなら自由に設定できますが、それを参照するのは対応するドライバです。ドライバが判るように設定してあげる必要があります。

kernel versionが新しくなるにつれ、ドキュメントも整備されていきました。kernel/Documentation/devicetree/bindings/ 配下に、ある程度まとめられています。善意の協力のもとで作られていますので、必ずしも欲しい情報が存在する/正しいとは限りません。

自分が商用向けで触ったりする場合には 可能な限りソースコードも参照することをおすすめします。

linux systemにおけるinitの最期

2018/12/18import
Linux Advent Calender 2018の18日目です。

元々の予定はボツになった!?ので ちょっと古い話になります。

7日目のsystemd理解のヒント - systemdの概念と歴史と相反するような記事です。
古い情報も必要としている組込もんもいるので、その助けとなりましたら幸いです。

initとは

PID=1として、カーネルの初期化が終わって初めて生成されるプロセスです。
ユーザランドで必要なファイルシステムのマウント・システム設定・サービス(デーモン)やプロセス起動を行います。

kernel parameterに init=でinitコマンドを指定することができます。省略すると /sbin/initなどが参照されます。
init=/etc/preinit
ここでは 組込装置を前提として、busyboxのinitを想定します

設定ファイル

initが起動すると、以下のファイルを参照します。run levelに応じた処理を記述するようになっています。
シリアルコンソールの設定なども、ここに記述することができます。
/etc/inittab
■/etc/inittab
FILE: 06.build/patches/rootfs/etc/inittab
# /etc/inittab
#
# Copyright (C) 2001 Erik Andersen <andersen@codepoet.org>
#
# Note: BusyBox init doesn't support runlevels.  The runlevels field is
# completely ignored by BusyBox init. If you want runlevels, use
# sysvinit.
#
# Format for each entry: <id>:<runlevels>:<action>:<process>
#
# id        == tty to run on, or empty for /dev/console
# runlevels == ignored
# action    == one of sysinit, respawn, askfirst, wait, and once
# process   == program to run

# Startup the system
::sysinit:/bin/mount -t proc proc /proc
::sysinit:/bin/mount -o remount,rw /
::sysinit:/bin/mkdir -p /dev/pts
::sysinit:/bin/mkdir -p /dev/shm
::sysinit:/bin/mount -a
::sysinit:/bin/hostname -F /etc/hostname
# now run any rc scripts
::sysinit:/etc/init.d/rcS

# Put a getty on the serial port
#ttyS0::respawn:/sbin/getty -L ttyS0 115200 vt100 # GENERIC_SERIAL

# Stuff to do for the 3-finger salute
#::ctrlaltdel:/sbin/reboot

# Stuff to do before rebooting
::shutdown:/etc/init.d/rcK
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r

システムの終了

reboot、halt、shutdownといったコマンドを実行すれば、システムを再起動・停止・終了することができますね。
このコマンドを実行したとき、何が行われているのでしょうか。

rebootに対象を絞って、かつ、busybox版で確認していきましょう。

v1.19.4
FILE: init/halt.c
//applet:IF_HALT(APPLET(halt, BB_DIR_SBIN, BB_SUID_DROP))
//applet:IF_HALT(APPLET_ODDNAME(poweroff, halt, BB_DIR_SBIN, BB_SUID_DROP, poweroff))
//applet:IF_HALT(APPLET_ODDNAME(reboot, halt, BB_DIR_SBIN, BB_SUID_DROP, reboot))
rebootは haltのaliasになるようですね。
int halt_main(int argc UNUSED_PARAM, char **argv)
|
...
	sleep(0);
	write_wtmp();  // last.logを記録する
	sync();        // デフォルトはsync(2)を実行する.

	kill(1,SIGTERM)	// initに signalを飛ばす. -f付きなら reboot(2)で 0x01234567 を渡す..
コメントに書いたようにsignalを飛ばすだけでした。
initに処理が移ります:

FILE: init/init.c

FUNC: int init_main(int argc UNUSED_PARAM, char **argv)
		bb_signals(0
			+ (1 << SIGUSR1) /* halt */
			+ (1 << SIGTERM) /* reboot */
			+ (1 << SIGUSR2) /* poweroff */
			, halt_reboot_pwoff);
signal handlerを登録します。bb_*は、Busyboxのライブラリ関数です。
第一引数で指定されたsignalを許可し、第二引数の関数をハンドラとして登録します。

ハンドラを見ていきましょう:
static void halt_reboot_pwoff(int sig)
{
	const char *m;
	unsigned rb;

	/* We may call run() and it unmasks signals,
	 * including the one masked inside this signal handler.
	 * Testcase which would start multiple reboot scripts:
	 *  while true; do reboot; done
	 * Preventing it:
	 */
	reset_sighandlers_and_unblock_sigs();
		// ハンドラをデフォルトに戻して, 有効化

	run_shutdown_and_kill_processes();
		// 後述. run_actrions(),全プロセスにSIGTERM, sync(2),sleep(1sec),SIGKILL,sync(2)

	m = "halt";
	rb = RB_HALT_SYSTEM;
	if (sig == SIGTERM) {
		m = "reboot";
		rb = RB_AUTOBOOT;
	} else if (sig == SIGUSR2) {
		m = "poweroff";
		rb = RB_POWER_OFF;
	}
	message(L_CONSOLE, "Requesting system %s", m);
	pause_and_low_level_reboot(rb);
		// 後述. sleep(1sec), vfork(2)した子で reboot(magic:0x1234567), 親(pid=1)はsleep forever.
	/* not reached */
}
いくつか関数を読んでいるので、引用します。コメントに書いたような処理を行っています。
static void run_shutdown_and_kill_processes(void)
{
	/* Run everything to be run at "shutdown".  This is done _prior_
	 * to killing everything, in case people wish to use scripts to
	 * shut things down gracefully... */
	run_actions(SHUTDOWN);

	message(L_CONSOLE | L_LOG, "The system is going down NOW!");

	/* Send signals to every process _except_ pid 1 */
	kill(-1, SIGTERM);
	message(L_CONSOLE | L_LOG, "Sent SIG%s to all processes", "TERM");
	sync();
	sleep(1);

	kill(-1, SIGKILL);
	message(L_CONSOLE, "Sent SIG%s to all processes", "KILL");
	sync();
	/*sleep(1); - callers take care about making a pause */
}
run_actions(SHUTDOWN);では、 inittabで記述した "::shutdown:"のコマンドを実行していきます。
swapoff, unmount -a -rが特徴的ですね。とくに後者は ファイルシステムをアンマウントすることで、
終了処理中に書き込みを行わせないようにしています(アンマウントできないときは
read onlyでリマウントするオプションが設定されています)。
static void pause_and_low_level_reboot(unsigned magic)
{
	pid_t pid;

	/* Allow time for last message to reach serial console, etc */
	sleep(1);

	/* We have to fork here, since the kernel calls do_exit(EXIT_SUCCESS)
	 * in linux/kernel/sys.c, which can cause the machine to panic when
	 * the init process exits... */
	pid = vfork();
	if (pid == 0) { /* child */
		reboot(magic);
		_exit(EXIT_SUCCESS);
	}
	while (1)
		sleep(1);
}
reboot(2)を実行しているようなので、libcを見ていきましょう。

libc

reboot(2)は以下のようにシステムコールを実行するだけでした。
/* Call kernel with additional two arguments the syscall requires.  */
int
reboot (int howto)
{
  return INLINE_SYSCALL (reboot, 3, (int) 0xfee1dead, 672274793, howto);
}
magic1: 0xfee1dead
magic2: 0x28121969

マジックナンバーが入っているけれども、これはカーネルソースで定義されている値ですね。

FILE: include/linux/reboot.h
/*
 * Magic values required to use _reboot() system call.
 */

#define LINUX_REBOOT_MAGIC1     0xfee1dead
#define LINUX_REBOOT_MAGIC2     672274793
#define LINUX_REBOOT_MAGIC2A    85072278
#define LINUX_REBOOT_MAGIC2B    369367448
#define LINUX_REBOOT_MAGIC2C    537993216

/*
 * Commands accepted by the _reboot() system call.
 *
 * RESTART     Restart system using default command and mode.
 * HALT        Stop OS and give system control to ROM monitor, if any.
 * CAD_ON      Ctrl-Alt-Del sequence causes RESTART command.
 * CAD_OFF     Ctrl-Alt-Del sequence sends SIGINT to init task.
 * POWER_OFF   Stop OS and remove all power from system, if possible.
 * RESTART2    Restart system using given command string.
 * SW_SUSPEND  Suspend system using software suspend if compiled in.
 * KEXEC       Restart system using a previously loaded Linux kernel
 */

#define LINUX_REBOOT_CMD_RESTART        0x01234567
#define LINUX_REBOOT_CMD_HALT           0xCDEF0123
#define LINUX_REBOOT_CMD_CAD_ON         0x89ABCDEF
#define LINUX_REBOOT_CMD_CAD_OFF        0x00000000
#define LINUX_REBOOT_CMD_POWER_OFF      0x4321FEDC
#define LINUX_REBOOT_CMD_RESTART2       0xA1B2C3D4
#define LINUX_REBOOT_CMD_SW_SUSPEND     0xD000FCE2
#define LINUX_REBOOT_CMD_KEXEC          0x45584543


kernel実装

(v4.4.120を参照しています)
システムコールから呼ばれる関数は以下ですね:

FILE: kernel/reboot.c
/*
 * Reboot system call: for obvious reasons only root may call it,
 * and even root needs to set up some magic numbers in the registers
 * so that some mistake won't make this reboot the whole machine.
 * You can also set the meaning of the ctrl-alt-del-key here.
 *
 * reboot doesn't sync: do that yourself before calling this.
 */
SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
		void __user *, arg)
	mutex_lock(&reboot_mutex);
	switch (cmd) {
	case LINUX_REBOOT_CMD_RESTART:
		kernel_restart(NULL);
		break;
..
	mutex_unlock(&reboot_mutex);
	return ret;
}
/**
 *	kernel_restart - reboot the system
 *	@cmd: pointer to buffer containing command to execute for restart
 *		or %NULL
 *
 *	Shutdown everything and perform a clean reboot.
 *	This is not safe to call in interrupt context.
 */
void kernel_restart(char *cmd)
{
	kernel_restart_prepare(cmd);
	migrate_to_reboot_cpu();
	syscore_shutdown();		// syscore_ops_listを走査する
	if (!cmd)
		printk(KERN_EMERG "Restarting system.\n");
	else
		printk(KERN_EMERG "Restarting system with command '%s'.\n", cmd);
	kmsg_dump(KMSG_DUMP_RESTART);
	machine_restart(cmd);
}
EXPORT_SYMBOL_GPL(kernel_restart);
void kernel_restart_prepare(char *cmd)
{
	blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd);
	system_state = SYSTEM_RESTART;
	usermodehelper_disable();		// timeout: 5sec
	device_shutdown();		// timeoutなし, devices_kset->list を走査する.
}
device_shutdown()と syscore_shutdown()の詳細は追い切れていませんが、
登録されているコールバック関数を呼び出して後処理をしているようです。
loadable moduleは removeされるような挙動を見かけたので、ここで解放されていると思われます(要確認)。

アーキテクチャ依存部: machine_restart()

armの場合は以下のとおり。
さらにプラットフォーム依存・ボード依存部で定義された関数呼び出しへと続きます。

FILE: arch/arm/kernel/reboot.c
void machine_restart(char *cmd)
{
	local_irq_disable();
	smp_send_stop();

	if (arm_pm_restart)
		arm_pm_restart(reboot_mode, cmd);
	else
		do_kernel_restart(cmd);

	/* Give a grace period for failure to restart of 1s */
	mdelay(1000);

	/* Whoops - the platform was unable to reboot. Tell the user! */
	printk("Reboot failed -- System halted\n");
	local_irq_disable();
	while (1);
}
このあたりは使用されているターゲットのコードを追いかけましょう。



参考まで、arm64の場合は以下のとおり。firmwareがUEFI対応であれば、それを使うようです。
FILE: arm64/kernel/process.c
/*
 * Restart requires that the secondary CPUs stop performing any activity
 * while the primary CPU resets the system. Systems with multiple CPUs must
 * provide a HW restart implementation, to ensure that all CPUs reset at once.
 * This is required so that any code running after reset on the primary CPU
 * doesn't have to co-ordinate with other CPUs to ensure they aren't still
 * executing pre-reset code, and using RAM that the primary CPU's code wishes
 * to use. Implementing such co-ordination would be essentially impossible.
 */
void machine_restart(char *cmd)
{
	/* Disable interrupts first */
	local_irq_disable();
	smp_send_stop();

	/*
	 * UpdateCapsule() depends on the system being reset via
	 * ResetSystem().
	 */
	if (efi_enabled(EFI_RUNTIME_SERVICES))
		efi_reboot(reboot_mode, NULL);

	/* Now call the architecture specific reboot code. */
	if (arm_pm_restart)
		arm_pm_restart(reboot_mode, cmd);
	else
		do_kernel_restart(cmd);

	/*
	 * Whoops - the architecture was unable to reboot.
	 */
	printk("Reboot failed -- System halted\n");
	while (1);
}

FILE: kernel/reboot.c
/**
 *	do_kernel_restart - Execute kernel restart handler call chain
 *
 *	Calls functions registered with register_restart_handler.
 *
 *	Expected to be called from machine_restart as last step of the restart
 *	sequence.
 *
 *	Restarts the system immediately if a restart handler function has been
 *	registered. Otherwise does nothing.
 */
void do_kernel_restart(char *cmd)
{
	atomic_notifier_call_chain(&restart_handler_list, reboot_mode, cmd);
}

奥が深いですね...(ぇ

arm-inline-asm

本コンテンツは、以下のサイトの文章を翻訳したものです。
おおむねごっごる先生による自動翻訳ですが、自分が判るレベルに修正しています。たぶん。
少しでもアセンブラ、インラインアセンブラの記述、読解に役立てれば幸いです。
This contents is translated from following web page.
It is almost translated by translate.google.co.jp. But I modify them to understand for me, maybe.
I hope you reading and writing assembler code with ARM. Let's enjoy!
http://www.ethernut.de/en/documents/arm-inline-asm.html


About this document

The GNU C compiler for ARM RISC processors offers, to embed assembly language code into C programs.
This cool feature may be used for manually optimizing time critical parts of the software or to use specific processor instruction, which are not available in the C language.
ARM RISCプロセッサー用のGNU Cコンパイラは、Cプログラムにアセンブリ言語コードを埋め込むことを提供します。
このクールな機能は、手動でソフトウェアのタイムクリティカルな部分を最適化するか、
C言語では利用できない特定のプロセッサ命令を使用するために使用することができます。
It's assumed, that you are familiar with writing ARM assembler programs, because this is not an ARM assembler programming tutorial.
It's not a C language tutorial either.
この記事は、ARMアセンブラプログラミングのチュートリアルではありませんので、
それは、あなたが書き込みARMアセンブラプログラムに精通していることを仮定します。
この記事は、C言語のチュートリアルでもありません。
All samples had been tested with GCC version 4, but most of them should work with earlier versions too.
すべてのサンプルは、GCCバージョン4でテストされていますが、
それらのほとんどは、以前のバージョンでも動作するでしょう。

GCC asm statement

Let's start with a simple example.
The following statement may be included in your code like any other C statement.
シンプルな例から始めましょう。
以下の文は、ほかのC文のようにあなたのコードに含まれていてもよいでしょう。
/* NOP example */
asm("mov r0,r0");
It moves the contents of register r0 to register r0.
In other words, it doesn't do much more than nothing.
It is also known as a NOP (no operation) statement and is typically used for very short delays.
これは、レジスタr0の内容を、レジスタr0へ移します。
言い換えると、何もしません。NOP(no operation)文として知られており、普通はとても短い遅延のために使われます。
Stop! Before adding this example right away to your C code, keep on reading and learn, why this may not work as expected.
待ってください!
この例を正しいあなたのCコードに追加する前に、何故期待したとおりに機能しない場合があるのか、読み進めて学んでください。
With inline assembly you can use the same assembler instruction mnemonics as you'd use for writing pure ARM assembly code.
And you can write more than one assembler instruction in a single inline asm statement.
To make it more readable, you can put each instruction on a separate line.
あなたは純粋なARMアセンブリコードを書くために使用したいようインラインアセンブリを使用すると、
同じアセンブラ命令ニーモニックを使用することができます。
そして、あなたは、単一のインラインasm分に複数のアセンブラ命令を記述することができます。
それを読みやすくするには、別の行に各命令を置くことができます。
asm(
"mov     r0, r0\n\t"
"mov     r0, r0\n\t"
"mov     r0, r0\n\t"
"mov     r0, r0"
);
The special sequence of linefeed and tab characters will keep the assembler listing looking nice.
It may seem a bit odd for the first time, but that's the way the compiler creates its own assembler code while compiling C statements.
改行やタブ文字の特別なシーケンスは、見栄えの良いアセンブラリストを維持します。
これは、初めて見ると少し奇妙に思えるかもしれないけれど、
Cステートメントのコンパイル中に、コンパイラが、独自のアセンブラコードを
作成する方法です。
So far, the assembler instructions are much the same as they'd appear in pure assembly language programs.
However, registers and constants are specified in a different way, if they refer to C expressions.
The general form of an inline assembler statement is
これまでのところ、アセンブラ命令は、彼らは純粋なアセンブリ言語プログラムで
表示されていたものとほとんど同じです。
ただし、Cの式を参照する場合、レジスタと定数は、異なる方法で指定されます。
インラインアセンブラ文の一般的な形式は、以下のとおりです:
asm(code : output operand list : input operand list : clobber list);
The connection between assembly language and C operands is provided by an optional second and third part of the asm statement, the list of output and input operands.
We will explain the third optional part, the list of clobbers, later.
アセンブリ言語とCオペランドとの間の接続は、asm文と、オプションの第二の部分(出力オペランドリスト)と
第三の部分(出力オペランドリスト)によって提供されています。
オプションの第三の部分(clobbersリスト)については後ほど説明します。
The next example of rotating bits passes C variables to assembly language.
It takes the value of one integer variable, right rotates the bits by one and stores the result in a second integer variable.
ビット回転の次の例では、アセンブリ言語にC変数を渡します。
これは、右に1ビット回転させ、第2の整数の変数に結果を格納し、1つの整数変数の値をとります。
/* Rotating bits example */
asm("mov %[result], %[value], ror #1" : [result] "=r" (y) : [value] "r" (x));
Each asm statement is divided by colons into up to four parts:
各asm文は、4つの部分までに、コロンで分割されます。
1.The assembler instructions, defined in a single string literal:
アセンブラ命令、1つの文字列リテラルで定義される
"mov %[result], %[value], ror #1"
An optional list of output operands, separated by commas.
Each entry consists of a symbolic name enclosed in square brackets, followed by a constraint string, followed by a C expression enclosed in parentheses.
Our example uses just one entry:
コンマで区切られた、オプションの出力オペランドリスト。
[result] "=r" (y)
A comma separated list of input operands, which uses the same syntax as the list of output operands.
Again, this is optional and our example uses one operand only:
コンマで区切られた、出力オペランドリストと同じような文法を使う、入力オペランドリスト。
[value] "r" (x)
Optional list of clobbered registers, omitted in our example.
オプションのclobberedレジスタリストは、この例では省略されています。
As shown in the initial NOP example, trailing parts of the asm statement may be omitted, if unused.
Inline asm statements containing assembler instruction only are also known as basic inline assembly, while statements containing optional parts are called extended inline assembly.
If an unused part is followed by one which is used, it must be left empty.
The following example sets the current program status register of the ARM CPU.
It uses an input, but no output operand.
最初のNOPの例に示すように、未使用の場合は、asm文の末尾部分は、省略してもよいです。
アセンブラ命令を含むインラインのasm文のみは、基本的なインラインアセンブリ(basic inline assembly)として知られている。
オプションパーツを含む文が拡張されたインラインアセンブリ(extended inline assembly)と呼ばれている。
未使用のオペランドリスト(からっぽ)に、使用されるオペランドリストが続く場合は、
(未使用の部分は)空のままにしておく必要があります。
次の例は、ARM CPUの現在のプログラムステータスレジスタを設定します。
入力オペランドはあるけれど、出力オペランドが無い例です:
asm("msr cpsr,%[ps]" : : [ps]"r"(status));
Even the code part may be left empty, though an empty string is required.
The next statement creates a special clobber to tell the compiler, that memory contents may have changed.
Again, the clobber list will be explained later, when we take a look to code optimization.
コード部分が空のままであっても、からの文字列を渡すことが要求されます。
次のステートメントは、特別なclobberをコンパイラに伝えます。それはメモリコンテンツが変化する、というものです。
クロバーリストは、後述します(コードの最適化を見るとき)。
asm("":::"memory");
You can insert spaces, newlines and even C comments to increase readability.
あなたは、読みやすさを向上させるために、スペース、改行、さらにはCのコメントを挿入することができます。
In the code section, operands are referenced by a percent sign followed by the related symbolic name enclosed in square brackets.
It refers to the entry in one of the operand lists that contains the same symbolic name.
From the rotating bits example:
%[result] refers to output operand, the C variable y, and

%[value] refers to the input operand, the C variable x.
コードセクションでは、オペランドは、角括弧で囲まれた関連シンボリック名が続くパーセント記号によって参照されます。
これは、同じシンボル名が含まれているオペランドリスト内の1つのエントリを参照します。
ローテートビットの例から:
%[result]出力オペランドを参照します。Cの変数はyです。
%[value]入力オペランドを参照します。Cの変数はxです。
Symbolic operand names use a separate name space.
That means, that there is no relation to any other symbol table.
To put it simple: You can choose a name without taking care whether the same name already exists in your C code.
However, unique symbols must be used within each asm statement.
シンボリックオペランド名が別々の名前空間を使用します。
それは、他のシンボルテーブルとは関係がないことを、意味します。
シンプルに置くには:同じ名前が既にあなたのCコードに存在するかどうか、気にせずに名前を選択することができます。
しかし、ユニークなシンボルを各asmステートメント内で使用する必要があります。
If you already looked to some working inline assembler statements written by other authors, you may have noticed a significant difference.
In fact, the GCC compiler supports symbolic names since version 3.1. For earlier releases the rotating bit example must be written as
既に他の著者によって書かれた、動いているインラインアセンブラ文を見た場合に、
有意な差に気づいているかもしれません。
実際には、GCCコンパイラは、バージョン3.1以降のシンボル名をサポートしています。
以前のリリースの場合、ローテートビットの例は次のように記述する必要があります
asm("mov %0, %1, ror #1" : "=r" (result) : "r" (value));
Operands are referenced by a percent sign followed by a single digit, where %0 refers to the first %1 to the second operand and so forth.
This format is still supported by the latest GCC releases, but quite error-prone and difficult to maintain.
Imagine, that you have written a large number of assembler instructions, where operands have to be renumbered manually after inserting a new output operand.
オペランドは、単一の数字が続くパーセント記号によって参照されます。%0は1つ目の、%1は2つ目のオペランド、のように。
このフォーマットは、まだ最新のGCCリリースによってサポートされていますが、非常にエラーが発生しやすく、メンテナンスも難しいです。。
あながた大きな数のアセンブラ命令を多数書いていたことを想像してみてください。
オペランドに新しい出力オペランドを挿入した後、手動で再番号付けしなくてはなりません。
If all this stuff still looks a little odd, don't worry.
Beside the mysterious clobber list, you have the strong feeling that something else is missing, right? Indeed, we didn't talk about the constraint strings in the operand lists.
I'd like to ask for your patience.
There's something more important to highlight in the next chapter.
すべてのこのようなものが、まだ少し奇妙に見える場合は、心配しないでください。
ミステリアスなクロバーリストのほかに、何か他のものが、
不足しているという強い気持ちを持っていますね?。
確かに、我々は、オペランドリストの制約文字列について話しませんでした。
私はあなたに辛抱をお願いしたいと思います。
次の章で強調表示するために、より重要な何かがあります。

C code optimization

There are two possible reasons why you want to use assembly language.
First is, that C is limited when we are getting closer to the hardware.
E.g. there's no C statement for directly modifying the processor status register.
The second reason is to create highly optimized code.
No doubt, the GNU C code optimizer does a good job, but the results are far away from handcrafted assembler code.
あなたはアセンブリ言語を使用する2つの理由があります。
我々は、ハードウェアに近づいているときに最初に、そのCが限られています。
例えば。直接プロセッサステータスレジスタを変更するにはCの文はありません。
第二の理由は、高度に最適化されたコードを作成することです。
間違いなく、GNU Cコードオプティマイザは、良い仕事をしていますが、結果は手作りのアセンブラコードにおよびません*1
The subject of the chapter is often overlooked: When adding assembly language code by using inline assembler statements, this code is also processed by the C compiler's code optimizer.
Let's examine the part of a compiler listing which may have been generated from our rotating bits example:
閑話休題。
インラインアセンブラ文を使用して、アセンブリ言語コードを追加する場合、このコードは、Cコンパイラのコード最適化によって処理されます。
ローテートビットの例から生成された可能性がある、コンパイラーリストの一部調べてみましょう:
00309DE5    ldr   r3, [sp, #0]    @ x, x
E330A0E1    mov   r3, r3, ror #1  @ tmp, x
04308DE5    str   r3, [sp, #4]    @ tmp, y
The compiler selected register r3 for bit rotation.
It could have selected any other register or two registers, one for each C variable.
It may not explicitly load the value or store the result.
Here is another listing, generated by a different compiler version with different compile options:
コンパイラは、レジスタr3をビットローテーションのために選択しました。
これは、任意の他のレジスタまたは二つのレジスタは、各C変数のための1つを選択している可能性があります。
明示的に値をロードしたり、結果を保存しない場合があります。
以下は、異なるコンパイルオプションを使用して、別のコンパイラのバージョンによって生成された別のリストです:
E420A0E1    mov r2, r4, ror #1    @ y, x
The compiler selected a unique register for each operand, using the value already cached in r4 and passing the result to the following code in r2.
Did you get the picture?
コンパイラは、それぞれのオペランドにユニークなレジスタを選択しました。
すでにレジスタr4にキャッシュされていた値を使い、結果を続くコード内のr2に渡します。
写真をとりましたか?
Often it becomes worse.
The compiler may even decide not to include your assembler code at all.
These decisions are part of the compiler's optimization strategy and depend on the context in which your assembler instructions are used.
For example, if you never use any of the output operands in the remaining part of the C program, the optimizer will most likely remove your inline assembler statement.
The NOP example we presented initially may be such a candidate as well, because to the compiler this is useless overhead, slowing down program execution.
多くの場合、悪くなります。
コンパイラは、まったくあなたのアセンブラコードが含まれないことを決定することができます。
これらの決定はコンパイラの最適化戦略の一部であり、あなたのアセンブラ命令が使用される文脈に依存します。
例えば、Cプログラムの残りの部分では、出力オペランドのいずれかを使用することがなければ、
オプティマイザは、おそらくあなたのインラインアセンブラ文を削除します。
最初に紹介したNOPの例は、このような候補になることがあります。
なぜならば、コンパイラにとって、これは無用のオーバーヘッドであるため、プログラムの実行が遅くなるためです。
The solution is to add the volatile attribute to the asm statement to instruct the compiler to exclude your assembler code from code optimization.
Remember, that you have been warned to use the initial example.
Here is the revised version:
解決策は、コード最適化から、あなたのアセンブラコードを除外しないよう、
コンパイラに命令するために、volatile属性をasm文に追加することです。
修正版を以下に示します:
/* NOP example, revised */
asm volatile("mov r0, r0");
But there is more trouble waiting for us.
A sophisticated optimizer will re-arrange the code.
The following C snippet had been left over after several last minute changes:
しかし、私たちを待っている多くの問題があります。
洗練されたオプティマイザがコードを再配置します。
次のCコードの断片は、いくつかの最後の分の変更後に残されていました:
i++;
if (j == 1)
    x += 3;
i++;
The optimizer will recognize, that the two increments do not have any impact on the conditional statement.
Furthermore it knows, that incrementing a value by 2 will cost one ARM instruction only.
Thus, it will re-arrange the code to
オプティマイザは次のように認識するでしょう。2つのインクリメントは、条件文に影響を及ぼさないだろう。
さらに、インクリメントによって値を2つ増やすことは、1つのARM命令のみよりコストがかかることを知っています。
したがって、以下のようにコードを再配置します。
if (j == 1)
    x += 3;
i += 2;
and save one ARM instruction.
As a result: There is no guarantee, that the compiled code will retain the sequence of statements given in the source code.
そして、ARM命令を1つ節約します。
結果:コンパイルされたコードがソースコード内で指定された一連の文を保持される保証はありません。
This may have a great impact on your code, as we will demonstrate now.
The following code intends to multiply c with b, of which one or both may be modified by an interrupt routine.
Disabling interrupts before accessing the variables and re-enable them afterwards looks like a good idea.
これは、以下にデモで示すように、貴方のコードに於いて、とても大きな影響があるでしょう。
以下のコードは、cをbで乗じます。1つまたは両法が、割込みルーチンで変更されるかもしれません。
変数アクセスの前に割込みを禁止し、処理後に再度有効にすると良さそうです。
asm volatile("mrs r12, cpsr\n\t"
    "orr r12, r12, #0xC0\n\t"
    "msr cpsr_c, r12\n\t" ::: "r12", "cc");
c *= b; /* This may fail. */
asm volatile("mrs r12, cpsr\n"
    "bic r12, r12, #0xC0\n"
    "msr cpsr_c, r12" ::: "r12", "cc");
Unfortunately the optimizer may decide to do the multiplication first and then execute both inline assembler instructions or vice versa.
This will make our assembly code useless.
不幸なことに、オプティマイザは、乗算を先に行い、その後、インラインアセンブラ命令を実行することを決定します。もしくは逆。
これはアセンブラコードを無意味にします。
We can solve this with the help of the clobber list, which will be explained now.
The clobber list from the example above
ここで解説するクロバーリストの助けにより、これを解決できます。
クロバーリストの例は上記のとおりで、以下の文字列です:
"r12", "cc"
informs the compiler that the assembly code modifies register r12 and updates the condition code flags.
Btw. using a hard coded register will typically prevent best optimization results.
In general you should pass a variable and let the compiler choose the adequate register.
Beside register names and cc for the condition register, memory is a valid keyword too.
It tells the compiler that the assembler instruction may change memory locations.
This forces the compiler to store all cached values before and reload them after executing the assembler instructions.
And it must retain the sequence, because the contents of all variables is unpredictable after executing an asm statement with a memory clobber.
アセンブラコードが、レジスタr12とcondition code flagsを更新することをコンパイラに情報を与えます。
ところで。通常、ハードコーディングされたレジスタを使用すると、最良の最適化の結果を防ぐことになります。
一般的には、変数を渡すと、コンパイラが適切なレジスタを選択するようにする必要があります。
レジスタ名と条件レジスタ用"cc"の他に、"memory"も有効なキーワードです。
これは、アセンブラ命令がメモリ位置を変更することがあることを、コンパイラに指示します。
前にすべてのキャッシュされた値を保存し、アセンブラ命令を実行した後にそれらを再ロードするようにコンパイラを強制します。
そして、それは、順序を保持しなければなりません。
"memory"クロバーでasm文を実行した後の、すべての変数の内容は予測不可能であるためです。
asm volatile("mrs r12, cpsr\n\t"
    "orr r12, r12, #0xC0\n\t"
    "msr cpsr_c, r12\n\t" :: : "r12", "cc", "memory");
c *= b; /* This is safe. */
asm volatile("mrs r12, cpsr\n"
    "bic r12, r12, #0xC0\n"
    "msr cpsr_c, r12" ::: "r12", "cc", "memory");
Invalidating all cached values may be suboptimal.
Alternatively you can add a dummy operand to create an artificial dependency:
すべてのキャッシュされた値を無効化することは次善の策なのかもしれません。
別の方法としては、人工的な依存関係を作成するために、ダミーのオペランドを追加することができます。
asm volatile("mrs r12, cpsr\n\t"
    "orr r12, r12, #0xC0\n\t"
    "msr cpsr_c, r12\n\t" : "=X" (b) :: "r12", "cc");
c *= b; /* This is safe. */
asm volatile("mrs r12, cpsr\n"
    "bic r12, r12, #0xC0\n"
    "msr cpsr_c, r12" :: "X" (c) : "r12", "cc");
This code pretends to modify variable b in the first asm statement and to use the contents variable c in the second.
This will preserve the sequence of our three statements without invalidating other cached variables.
このコードは、最初のasmステートメントに変数bを変更すると第二のコンテンツを変数cを使用するフリをします。
これは、他のキャッシュされた変数を無効にすることなく、私たちの3つの文の順序を保持します。
It is essential to understand how the optimizer affects inline assembler statements.
If something remains nebulous, better re-read this part before moving on the the next topic.
これは、オプティマイザは、インラインアセンブラ文にどのように影響するかを理解することが不可欠です。
何かが漠然としたままの場合は、より良い次のトピックに移動する前に、この部分を再度読んでください。。

*1 : 訳注:ずいぶんと良くなっているので、どうかなぁ。vectorizeまでやってるものなぁ。

Input and output operands

We learned, that each input and output operand is described by a symbolic name enclosed in square bracket,
followed by a constraint string, which in turn is followed by a C expression in parentheses.
私たちは、それぞれの入力および出力オペランドが、順番に括弧内のCの式が続く制約文字列が続く、
角括弧で囲まれたシンボル名で記述されていること、を学びました。
What are these constraints and why do we need them?
You probably know that every assembly instruction accepts specific operand types only.
For example, the branch instruction expects a target address to jump at.
However, not every memory address is valid, because the final opcode accepts a 24-bit offset only.
In contrary, the branch and exchange instruction expects a register that contains a 32-bit target address.
In both cases the operand passed from C to the inline assembler may be the same C function pointer.
Thus, when passing constants, pointers or variables to inline assembly statements, the inline assembler must know, how they should be represented in the assembly code.
どのようなこれらの制約があり、我々にはなぜそれが必要なのですか?
あなたは、おそらくすべてのアセンブリ命令は、特定のオペランドのタイプを受け入れることを知っています。
例えば、分岐命令がでジャンプするターゲットアドレスを期待しています。
しかし、全てのメモリアドレスが有効ではありません。最終的にオペコードは24bitオフセットのみを許容するからです。
逆に、分岐交換命令は、32ビット・ターゲット・アドレスを含むレジスタを期待しています。
どちらの場合も、インラインアセンブラにCから渡されたオペランドは、同じCの関数ポインタである場合があります。
したがって、定数やポインタや変数をインラインアセンブリ分文へ渡すとき、
インラインアセンブラは、それらがアセンブリコードで表現する方法を、知っている必要があります。
For ARM processors, GCC 4 provides the following constraints.
ARMプロセッサ用のGCC4は、以下の制約を提供します:
Constraint Usage in ARM state Usage in Thumb state
fFloating point registers f0 .. f7Not available
hNot availableRegisters r8..r15
GImmediate floating point constantNot available
HSame a G, but negatedNot available
IImmediate value in data processing instructions
e.g. ORR R0, R0, #operand
Constant in the range 0 .. 255
e.g. SWI operand
JIndexing constants -4095 .. 4095
e.g. LDR R1, [PC, #operand]
Constant in the range -255 .. -1
e.g. SUB R0, R0, #operand
KSame as I, but invertedSame as I, but shifted
LSame as I, but negatedConstant in the range -7 .. 7
e.g. SUB R0, R1, #operand
lSame as rRegisters r0..r7
e.g. PUSH operand
MConstant in the range of 0 .. 32 or a power of 2
e.g. MOV R2, R1, ROR #operand
Constant that is a multiple of 4 in the range of 0 .. 1020
e.g. ADD R0, SP, #operand
mAny valid memory address
NNot availableConstant in the range of 0 .. 31
e.g. LSL R0, R1, #operand
ONot availableConstant that is a multiple of 4 in the range of -508 .. 508
e.g. ADD SP, #operand
rGeneral register r0 .. r15
e.g. SUB operand1, operand2, operand3
Not available
wVector floating point registers s0 .. s31Not available
XAny operand
Constraint characters may be prepended by a single constraint modifier.
Constraints without a modifier specify read-only operands.
Modifiers are:
制約文字は単一の制約修飾子で、前に付加することができます。
修飾子のない制約は、読み取り専用のオペランドを指定します。
修飾子は以下のとおり:
Modifier Specifies
=Write-only operand, usually used for all output operands
+Read-write operand, must be listed as an output operand
&A register that should be used for output only
Output operands must be write-only and the C expression result must be an lvalue, which means that the operands must be valid on the left side of assignments.
The C compiler is able to check this.
出力オペランドは書き込み専用にし、Cの式の結果は左辺値でなければなりません。
これは、オペランドは代入の左辺で有効である必要があることを意味します。。
Cコンパイラは、これを確認することができます。
Input operands are, you guessed it, read-only.
Note, that the C compiler will not be able to check, whether the operands are of reasonable type for the kind of operation used in the assembler instructions.
Most problems will be detected during the late assembly stage, which is well known for its weird error messages.
Even if it claims to have found an internal compiler problem that should be immediately reported to the authors, you better check your inline assembler code first.
入力オペランドは、想像どおり、読込専用です。
Cコンパイラは、オペランドがアセンブラ命令で使用するのに妥当な型か、を、チェックできないことに注意してください。
ほとんどの問題はよく、後半の組立段階中に検出され、その奇妙なエラーメッセージのために知られています。
すぐに作者に報告すべき内部コンパイラの問題を発見した、と、主張している場合でも、
あなたは最初に、インラインアセンブラコードを確認したほうが良いです。
A strict rule is: Never ever write to an input operand.
But what, if you need the same operand for input and output?
The constraint modifier + does the trick as shown in the next example:
厳格なルールは次のとおりです:"これまでに入力オペランドに書き込むことはありません"。
しかし、あなたが入力と出力のための同じオペランドが必要な場合は?
制約修飾子"+"は、次の例に示すように、トリックを行います。
asm("mov %[value], %[value], ror #1" : [value] "+r" (y));
This is similar to our rotating bits example presented above.
It rotates the contents of the variable value to the right by one bit.
In opposite to the previous example, the result is not stored in another variable.
Instead the original contents of input variable will be modified.
前述のローテートビットの例に酷似しています。
これは変数の値のコンテンツを、右に1ビットだけローテートします。
前の例とは逆に、結果が別の変数に格納されていません。
代わりに、入力変数の元の内容が変更されます。
The modifier + may not be supported by earlier releases of the compiler.
Luckily they offer another solution, which still works with the latest compiler version.
For input operators it is possible to use a single digit in the constraint string.
Using digit n tells the compiler to use the same register as for the n-th operand, starting with zero.
Here is an example:
修飾子"+"は、コンパイラの以前のリリースではサポートされない場合があります。
幸いなことに、彼らはまだ最新のコンパイラのバージョンで動作する別のソリューションを提供します。
入力オペレータにとっては、制約文字列に単一の数字を使用することが可能です。
数字nを使用すると、ゼロから始まるn番目のオペランドのためのと同じレジスタを使用するようにコンパイラに指示します。
asm("mov %0, %0, ror #1" : "=r" (value) : "0" (value));
Constraint "0" tells the compiler, to use the same input register that is used for the first output operand.
制約"0"は、コンパイラに、最初の出力オペランドで使われたレジスタと同じ入力レジスタを使うことを伝えます。
Note however, that this doesn't automatically imply the reverse case.
The compiler may choose the same registers for input and output, even if not told to do so.
You may remember the first assembly listing of the rotating bits example with two variables, where the compiler used the same register r3 for both variables.
The asm statement
これは、自動的に逆の場合を意味するものではありませんので、注意してください。
そうするように言われていない場合でも、コンパイラは、入力と出力に同じレジスタを選択することができます。
あなたは、コンパイラは両方の変数に同じレジスタr3を使用し、
2つの変数で回転ビットの例の最初のアセンブリー・リストを覚えています:
asm("mov %[result],%[value],ror #1":[result] "=r" (y):[value] "r" (x));
generated this code:
00309DE5    ldr   r3, [sp, #0]    @ x, x
E330A0E1    mov   r3, r3, ror #1  @ tmp, x
04308DE5    str   r3, [sp, #4]    @ tmp, y
This is not a problem in most cases, but may be fatal if the output operator is modified by the assembler code before the input operator is used.
In situations where your code depends on different registers used for input and output operands, you must add the & constraint modifier to your output operand.
The following code demonstrates this problem.
これは、ほとんどの場合、問題ではありませんが、入力演算子が使用される前に出力オペレータは、
アセンブラコードによって変更された場合、致命的なことがあります。
あなたのコードは、入力と出力オペランドに使用する異なるレジスタに依存する状況では、
あなたの出力オペランドに"&"制約修飾子を追加する必要があります。
次のコードは、この問題を示します。
asm volatile("ldr %0, [%1]"     "\n\t"
             "str %2, [%1, #4]" "\n\t"
             : "=&r" (rdv)
             : "r" (&table), "r" (wdv)
             : "memory");
A value is read from a table and then another value is written to another location in this table.
If the compiler would have chosen the same register for input and output, then the output value would have been destroyed on the first assembler instruction.
Fortunately, the & modifier instructs the compiler not to select any register for the output value, which is used for any of the input operands.
値は、テーブルから読み取られ、次に別の値は、このテーブル内の別の場所に書き込まれます。
コンパイラは、入力と出力に同じレジスタを選択したことになる場合には、出力値は最初のアセンブラ命令で破壊されたと推定します。
幸いなことに、"&"修飾子は、入力オペランドのいずれかのために使用される出力値のための任意のレジスタを選択しないようにコンパイラに指示します。

More recipes

Inline assembler as preprocessor macro(プリプロセッサマクロとしてのインラインアセンブラ)

In order to reuse your assembler language parts, it is useful to define them as macros and put them into include files.
Using such include files may produce compiler warnings, if they are used in modules, which are compiled in strict ANSI mode.
To avoid that, you can write __asm__ instead of asm and __volatile__ instead of volatile.
These are equivalent aliases.
アセンブラ言語の部分を再利用するためには、マクロとして定義し、includeファイルにそれらを置くことが有用である。
そのようなインクルードファイルを使用することは、厳密なANSIモードでコンパイルされたモジュールで使用されている場合、コンパイラの警告を生成することがあるでしょう。
それを回避するには、asmの代わりに__asm__で、volatileの代わりに__volatile__と書くことができます。
これらは同等の別名(alias)です。
Here is a macro which will convert a long value from little endian to big endian or vice versa:
ここでは、リトルエンディアンからビッグエンディアンに、またはその逆にlong値を変換するマクロをしめします:
#define BYTESWAP(val) \
    __asm__ __volatile__ ( \
        "eor     r3, %1, %1, ror #16\n\t" \
        "bic     r3, r3, #0x00FF0000\n\t" \
        "mov     %0, %1, ror #8\n\t" \
        "eor     %0, %0, r3, lsr #8" \
        : "=r" (val) \
        : "0"(val) \
        : "r3", "cc" \
    );

C stub functions

Macro definitions will include the same assembler code whenever they are referenced.
This may not be acceptable for larger routines.
In this case you may define a C stub function.
マクロ定義は、それらが参照されるたびに同じアセンブラコードが含まれます。
マクロ定義は、自身を参照するアセンブラコードを含むでしょう。
これは大きなルーチンでは許容されない場合があります。
このようなケースでは、Cのスタブ関数を定義することができます。
Here is the byte swap procedure again, this time implemented as a C function.
ここでは、バイトスワップ手順を再び示します:この時、Cの関数として実装します。
unsigned long ByteSwap(unsigned long val)
{
asm volatile (
        "eor     r3, %1, %1, ror #16\n\t"
        "bic     r3, r3, #0x00FF0000\n\t"
        "mov     %0, %1, ror #8\n\t"
        "eor     %0, %0, r3, lsr #8"
        : "=r" (val)
        : "0"(val)
        : "r3"
);
return val;
}

Replacing symbolic names of C variables(Cの変数をシンボリックに置換する)

By default GCC uses the same symbolic names of functions or variables in C and assembler code.
You can specify a different name for the assembler code by using a special form of the asm statement:
デフォルトでは、GCCは、Cとアセンブラコード内の関数や変数の同じシンボル名を使用しています。
あなたはasm文の特別なフォームを使用してアセンブラコードに別の名前を指定することができます。
unsigned long value asm("clock") = 3686400;
This statement instructs the compiler to use the symbolic name clock rather than value.
This makes sense only for global variables.
Local variables (aka auto variables) do not have symbolic names in assembler code.
この文は、値ではなく、シンボリック名の"clock"を使用するようにコンパイラに指示します。
これは、グローバル変数のために理にかなっています。
ローカル変数(自動変数)は、アセンブラコードのシンボル名を持っていません。
Replacing symbolic names of C functions
In order to change the name of a function, you need a prototype declaration, because the compiler will not accept the asm keyword in the function definition:
C関数のシンボリック名を置き換えます
コンパイラは、関数定義のasmキーワードを受け入れないので、関数の名前を変更するためには、あなたは、プロトタイプ宣言が必要になります。
extern long Calc(void) asm ("CALCULATE");
Calling the function Calc() will create assembler instructions to call the function CALCULATE.
関数Calc()を呼び出す際には、関数CALCULATEを呼び出すためのアセンブラ命令を作成します。

Forcing usage of specific registers(指定レジスタの使用を強制する)

A local variable may be held in a register.
You can instruct the inline assembler to use a specific register for it.
ローカル変数は、レジスタに保持されることがあります。
あなたはそれのために特定のレジスタを使用するインラインアセンブラに指示することができます。
void Count(void) {
register unsigned char counter asm("r3");

... some code...
asm volatile("eor r3, r3, r3" : "=l" (counter));
... more code...
}
The assembler instruction, "eor r3, r3, r3", will clear the variable counter.
Be warned, that this sample is bad in most situations, because it interferes with the compiler's optimizer.
Furthermore, GCC will not completely reserve the specified register.
If the optimizer recognizes that the variable will not be referenced any longer, the register may be re-used.
But the compiler is not able to check whether this register usage conflicts with any predefined register.
If you reserve too many registers in this way, the compiler may even run out of registers during code generation.
アセンブラ命令、"eor r3, r3, r3"は、変数カウンタをクリアします。
これはコンパイラの最適化に干渉するため、このサンプルでは、ほとんどの状況で悪いことを警告されます。
また、GCCは完全に指定されたレジスタを予約しません。
オプティマイザは、変数がもはや参照されないことを認識した場合、レジスタを再使用することができます。
しかし、コンパイラは、任意の事前定義されたレジスタと、このレジスタ使用するかどうかの競合を確認することができません。
あなたはこのようにあまりにも多くのレジスタを予約した場合、コンパイラはあっても、コード生成時レジスタが不足することがあります。

Using registers temporarily

If you are using registers, which had not been passed as operands, you need to inform the compiler about this.
The following code will adjust a value to a multiple of four.
It uses r3 as a scratch register and lets the compiler know about this by specifying r3 in the clobber list.
Furthermore the CPU status flags are modified by the ands instruction and thus cc had been added to the clobbers.
あなたがオペランドとして渡されていないレジスタを使用している場合は、このことについてコンパイラに通知する必要があります。
次のコードは、4の倍数に値を調整します。
これは、スクラッチ・レジスタとしてr3を使用し、コンパイラはクロバーリストにr3を指定することで、これについて知ることができます。
さらに、CPUのステータスフラグは論理積命令によって変更されているので、ccがクロバーリストに追加されています。
asm volatile(
    "ands    r3, %1, #3"     "\n\t"
    "eor     %0, %0, r3" "\n\t"
    "addne   %0, #4"
    : "=r" (len)
    : "0" (len)
    : "cc", "r3"
  );
Again, hard coding register usage is always bad coding style.
Better implement a C stub function and use a local variable for temporary values.
再度書きますが、ハードレジスタの使用をコーディングすることは、常に悪いコーディングスタイルです。
より良いCスタブ機能を実装し、一時的な値については、ローカル変数を使用します。

Using constants(定数を使う)

You can use the mov instruction to load an immediate constant value into a register.
Basically, this is limited to values ranging from 0 to 255.
あなたはレジスタに即値定数値をロードするmov命令を使用することができます。
基本的に、これは、0から255の範囲の値に制限されています。
asm("mov r0, %[flag]" : : [flag] "I" (0x80));
But also larger values can be used when rotating the given range by an even number of bits.
In other words, any result of

n * 2^x

with n is in the mentioned range of 0 to 255 and x is an even number in the range of 0 to 24.
Because of rotation, x may be set to 26, 28 or 30, in which case bits 37 to 32 are folded to bits 5 to 0 resp.
Last not least, the binary complement of these values may be given, when using mvn instead of mov.
偶数のビット数だけ回転させた範囲で、より大きな値を使用することができます。
言い換えると、次の任意の結果を使うことができます:
n * 2^x : nは0から255の値で, xは0から24の偶数
回転なので、ビット37から32は、それぞれビット5から0に折りたたまれても良いような場合には、26,28,30に設定することができます。
movのかわりにmvnを使う時は、これらの値の2の補数は与えられてもよいです。
Sometimes you need to jump to a fixed memory address, which may be defined by a preprocessor macro.
You can use the following assembly code:
時々、プリプロセッサマクロに依って定義された固定メモリアドレスへジャンプしたい時があります。
以下のアセンブリコードを使うことができます:
    ldr  r3, =JMPADDR
    bx   r3
This will work with any legal address value.
If the constant fits (for example 0x20000000), then the smart assembler will convert this to
これは任意の有効なアドレス値で動作します。
定数がフィットするならば(例えば0x20000000)、賢いアセンブラは以下のように変換するでしょう:
    mov  r3, #0x20000000
    bx   r3
If it doesn't fit (for example 0x00F000F0), then the assembler will load the value from the literal pool.
もしフィットしないなら(例えば、0x00F000F0)、アセンブラはリテラルプールから値を読みだすでしょう:
    ldr  r3, .L1
    bx   r3
    ...
    .L1: .word 0x00F000F0
With inline assembly it works in the same way.
But instead of using ldr, you can simply provide a constant as a register value:
インラインアセンブリでも、同様にふるまいます。
しかし、ldrを使う代わりに、レジスタ値のように定数を簡単に与えることができます
asm volatile("bx %0" : : "r" (JMPADDR));
Depending on the actual value of the constant, either mov, ldr or any of its variants is used.
If JMPADDR is defined as 0xFFFFFF00, then the resulting code will be similar to
定数の実際の値に依存して、ldrまたは、その変形が使われます。
JMPADDRが、0xFFFFFF00と定義されているなら、結果のコードは以下のようになるでしょう:
    mvn  r3, #0xFF
    bx   r3
The real world is more complicated.
It may happen, that we need to load a specific register with a constant.
Let's assume, that we want to call a subroutine, but we want to return to another address than the one that follows our branch.
This is can be useful when embedded firmware returns from main.
In this case we need to load the link register. Here is the assembly code:
現実では、もっと複雑です。指定されたレジスタに定数をロードする必要が起きるでしょう。
サブルーチン呼び出しをしたいけれども、分岐の続きではなく、他のアドレスへ戻りたいと仮定しましょう。
これは、組み込みファームウェアが、mainから戻るときに使えます。
このケースでは、リンクレジスタに読み出す必要があります。
アセンブリコードを示します:
    ldr  lr, =JMPADDR
    ldr  r3, main
    bx   r3
Any idea how to implement this in inline assembly? Here is a solution:
インラインアセンブリで実装するアイデアはあるでしょうか。
解決法を示します:
asm volatile(
    "mov lr, %1\n\t"
    "bx %0\n\t"
    : : "r" (main), "I" (JMPADDR));
But there is still a problem.
We use mov here and this will work as long as the value of JMPADDR fits.
The resulting code will be the same than what we get in pure assembly code.
If it doesn't fit, then we need ldr instead.
But unfortunately there is no way to express in inline assembly.
しかし、少し問題があります。
ここでmov命令を使い、JMPADDRがフィットする間は動きます。
結果のコードは、ピュアアセンブリコードで得たものと同じになるでしょう。
JMPADDRがフィットしない場合、ldr命令に変えなければなりません。
しかし、不幸なことにインラインアセンブリで表現する手段はありません。
    ldr  lr, =JMPADDR
Instead, we must write
代わりに、以下のように書かなければなりません。
asm volatile(
    "mov lr, %1\n\t"
    "bx %0\n\t"
    : : "r" (main), "r" (JMPADDR));
Compared to the pure assembly code, we end up with an additional statement, using an additional register.
ピュアアセンブリコードと比べましょう。
追加の分と追加のレジスタを使って終わります。
    ldr     r3, .L1
    ldr     r2, .L2
    mov     lr, r2
    bx      r3

Register Usage(レジスタの使い方)

It is always a good idea to analyze the assembly listing output of the C compiler and study the generated code.
The following table of the compiler's typical register usage will be probably helpful to understand the code.
常にCコンパイラのアセンブリ出力リストを分析し、生成されたコードを勉強することをお勧めします。
コンパイラの典型的なレジスタの使用の次の表は、コードを理解するために、おそらく参考になります。
Register Alt.Name Usage
r0a1First function argument Integer function result Scratch register
r1a2Second function argument Scratch register
r2a3Third function argument Scratch register
r3a4Fourth function argument Scratch register
r4v1Register variable
r5v2Register variable
r6v3Register variable
r7v4Register variable
r8v5Register variable
r9v6Register variable
rfpReal frame pointer
r10slStack limit
r11fpArgument pointer
r12ipTemporary workspace
r13spStack pointer
r14lrLink register Workspace
r15pcProgram counter

Common pitfalls

Instruction sequence

Developers often expect, that a sequence of instructions remains in the final code as specified in the source code.
This assumption is wrong and often introduces hard to find bugs.
Actually, asm statements are processed by the optimizer in the same way as other C statements.
They may be rearranged if dependencies allow this.
開発者は、多くの場合、ソースコードで指定された命令のシーケンスは、最終的なコードに残っていることを、期待しています。
この仮定が間違っていると、多くの場合、バグを見つけるのは難しいことを紹介します。
実際に、asm文が他のC言語の文と同じように、オプティマイザによって処理されています。
依存関係がこれを許可した場合、asm文は再配置することができます。
The chapter "C code optimization" discusses the details and offers solutions.
"C code optimization"の章で、詳細を議論し、解決法を提案しています。

Defining a variable as a specific register

Even if a variable had been forcibly assigned to a specific register, the resulting code may not work as expected.
Consider the following snippet:
変数を強制的に特定のレジスタに割り当てられていた場合であっても、予想通り、結果のコードは動作しない可能性があります。
以下のコードを考えてみます。
int foo(int n1, int n2) {
  register int n3 asm("r7") = n2;
  asm("mov r7, #4");
  return n3;
}
The compiler is instructed to use r7 as a local variable n3, which is initialized by parameter n2.
Then the inlined assembly statement sets r7 to 4, which should be finally returned.
However, this may go completely wrong.
Remember, that compiler cannot recognize, what's happening inside the inline assembly.
But the optimizer is smart on the C code, generating the following assembly code.
コンパイラは、パラメータn2で初期化されているローカル変数n3としてr7を使用するように指示されます。
そして、インラインアセンブリステートメントがr7に4をセットし、最終的に返されるべきである。
しかし、これは完全に間違って行くことがあります。
そのコンパイラは、インラインアセンブリの内部で何が起こっているか、認識できない、ということを、覚えておいてください。
しかし、オプティマイザは、Cコードに賢く、次のアセンブリコードを生成します:
foo:
  mov r7, #4
  mov r0, r1
  bx  lr
Instead of returning r7, the value of n2 is returned, which had been passed to our function in r1.
What happed here? Well, while the final code still contains our inline assembly statement, the C code optimizer decided, that n3 is not required.
It directly returns parameter n2 instead.
r7を返す代わりに、私たちの関数に渡されていたr1に入っているn2の値が返されます。
ここで何が起こったのか?
最終的なコードは、まだ私達のインラインアセンブリ文は、決定したCのコード最適化が含まれていながら、まあ、そのn3は必要ありません。
代わりに直接パラメータn2を返します。
Just assigning a variable to a fixed register does not mean, that the C compiler will use that variable.
We still have to tell the compiler, that a variable is modified inside the inline assembly operation.
For the given example, we need to extend the asm statement with an output operator:
固定レジスタに変数を割り当てるとが、Cコンパイラはその変数を使用すること、を直ちに意味するものではありません。
まだ、変数はインラインアセンブリ演算の内部で修正されていることを、コンパイラに指示する必要があります。
与えられた例では、出力演算子でasm文を拡張する必要があります。
asm("mov %0, #4" : "=l" (n3));
Now the C compiler is aware, that n3 is modified and will generate the expected result:
今、Cコンパイラが認識して、n3が変更されると予想される結果を生成します:
foo:
  push {r7, lr}
  mov  r7, #4
  mov  r0, r7
  pop  {r7, pc}

Executing in Thumb status

Be aware, that, depending on the given compile options, the compiler may switch to thumb state.
Using inline assembler with instructions that are not available in thumb state will result in cryptic compile errors.
指定されたコンパイルオプションに応じて、コンパイラがThumb状態に切り替えることができる、ということに注意してください。
Thumb状態では使用できない命令でインラインアセンブラを使用すると、不可解なコンパイルエラーになります。

Assembly code size

In most cases the compiler will correctly determine the size of the assembler instruction, but it may become confused by assembler macros.
Better avoid them.
ほとんどの場合、コンパイラは正しくアセンブラ命令のサイズを決定しますが、それはアセンブラマクロによって混乱することがあります。
それらを避けることがより良いです。
In case you are confused: This is about assembly language macros, not C preprocessor macros.
It is fine to use the latter.
あなたが混乱しているケース:これは、アセンブリ言語のマクロではなく、Cプリプロセッサマクロについてです。
後者を使用するのがよいです。

Labels

Within the assembler instruction you can use labels as jump targets.
However, you must not jump from one assembler instruction into another.
The optimizer knows nothing about those branches and may generate bad code.
アセンブラ命令内では、ジャンプターゲットとしてラベルを使用することができます。
しかし、あなたは別のものに1アセンブラ命令からジャンプしてはなりません。
オプティマイザは、これらの分岐について何も知らないし、不正なコードを生成することがあります。

Preprocessor macros

Inline assembly instruction cannot contain preprocessor macros, because for the preprocessor these instruction are nothing else but string constants.
プリプロセッサのために、これらの命令は何もないが、文字列定数であるため、
インラインアセンブリ命令は、プリプロセッサマクロを含めることはできません。
If your assembly code must refer to values that are defined by macros, see the chapter about "Using constants" above.
あなたのアセンブリコードは、マクロによって定義された値を参照する必要がある場合は、
上記の"Using constants"に関する章を参照してください。

External links

For a more thorough discussion of inline assembly usage, see the gcc user manual.
The latest version of the gcc manual is always available here:
http://gcc.gnu.org/onlinedocs/
インラインアセンブリの使用状況についての全体的な説明については、gccのマニュアルを参照してください。
gccのマニュアルの最新バージョンは、以下で常に利用可能です:
http://gcc.gnu.org/onlinedocs/

Copyright

Copyright (C) 2007-2013 by Harald Kipp.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3
or any later version published by the Free Software Foundation.
If you think that something hasn't been explained clearly enough or is even wrong, please let me know.

Document History

Date (YMD} Change Thanks to
2014/02/11Fixed the first constant example, where the constant must be an input operand.spider391Tang
2013/08/16Corrected the example code of specific register usage and added a new pitfall section about the same topic.Sven Köhler
2012/03/28Corrected the pitfall section about constant parameters and moved to the usage section.enh
Added a preprocessor macros pitfall.
Added this history.

procfsのtask statは何を示すのか

2015/12/12linuximport

Advent calender2015

今年も書かせていただくことにしました。比較的まともに?記事を書くことのできるトリガーとして助かります。

Linux kernel vanilla v4.2

本記事では、著者の技量の都合により語彙が適当では無いことがあります。指摘いただけますと助かります。


procfsで見えるもの

稼働しているシステムで動いているプロセスを調べるために、psコマンドを使っていますよね。busybox版のソースコードを見ると判りやすいのですが、/proc配下のファイルを参照して情報を集めているのです。/procは、procfsがマウントされており、物理記憶装置上のファイルシステムではなく、Linux kernelとユーザランドとをつないでいるインタフェースでもあります。

タスク情報

ユーザランドでは、プロセス、スレッドと区別していますが、kernelではいずれも"task struct"で管理されます。スレッドは、libc実装ではプロセス空間を共有するtaskと考えるとよいでしょう。forkするときは注意が必要なのですが、これはまた別の機会があればそちらで。。。

本題のプロセス情報、以下のディレクトリ配下にあるファイルから取得ができます。

/proc/<id>/

このファイルを通して読み書きされるコードは、procfsのところでも触れたとおり、kernelのフレームワークを利用すると簡潔なコードになります。とりあえずソースコードを見てみましょう:

FILE: fs/proc/base.c

static const struct pid_entry tid_base_stuff[] = {
	ONE("stat",      S_IRUGO, proc_tid_stat),
	ONE("statm",     S_IRUGO, proc_pid_statm),
	REG("maps",      S_IRUGO, proc_tid_maps_operations),

statにpsで表示するための情報がたくさん含まれているようです(busybox版psコマンドソースコードより):

int proc_tid_stat(struct seq_file *m, struct pid_namespace *ns,
			struct pid *pid, struct task_struct *task)
{
	return do_task_stat(m, ns, pid, task, 0);
}

FILE: fs/proc/array.c

static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
			struct pid *pid, struct task_struct *task, int whole)

ここをざーっと眺めていくと、なにを表示しているのかがわかりますね!(ブン投げ

/proc/PID/statの表示コード(変数はこの手前に代入されている)

	seq_printf(m, "%d (%s) %c", pid_nr_ns(pid, ns), tcomm, state);
	seq_put_decimal_ll(m, ' ', ppid);
	seq_put_decimal_ll(m, ' ', pgid);
	seq_put_decimal_ll(m, ' ', sid);
	seq_put_decimal_ll(m, ' ', tty_nr);
	seq_put_decimal_ll(m, ' ', tty_pgrp);
	seq_put_decimal_ull(m, ' ', task->flags);
	seq_put_decimal_ull(m, ' ', min_flt);
	seq_put_decimal_ull(m, ' ', cmin_flt);
	seq_put_decimal_ull(m, ' ', maj_flt);
	seq_put_decimal_ull(m, ' ', cmaj_flt);
	seq_put_decimal_ull(m, ' ', cputime_to_clock_t(utime));
	seq_put_decimal_ull(m, ' ', cputime_to_clock_t(stime));
	seq_put_decimal_ll(m, ' ', cputime_to_clock_t(cutime));
	seq_put_decimal_ll(m, ' ', cputime_to_clock_t(cstime));
	seq_put_decimal_ll(m, ' ', priority);
	seq_put_decimal_ll(m, ' ', nice);
	seq_put_decimal_ll(m, ' ', num_threads);
	seq_put_decimal_ull(m, ' ', 0);
	seq_put_decimal_ull(m, ' ', start_time);
	seq_put_decimal_ull(m, ' ', vsize);
	seq_put_decimal_ull(m, ' ', mm ? get_mm_rss(mm) : 0);
	seq_put_decimal_ull(m, ' ', rsslim);
	seq_put_decimal_ull(m, ' ', mm ? (permitted ? mm->start_code : 1) : 0);
	seq_put_decimal_ull(m, ' ', mm ? (permitted ? mm->end_code : 1) : 0);
	seq_put_decimal_ull(m, ' ', (permitted && mm) ? mm->start_stack : 0);
	seq_put_decimal_ull(m, ' ', esp);
	seq_put_decimal_ull(m, ' ', eip);
	/* The signal information here is obsolete.
	 * It must be decimal for Linux 2.0 compatibility.
	 * Use /proc/#/status for real-time signals.
	 */
	seq_put_decimal_ull(m, ' ', task->pending.signal.sig[0] & 0x7fffffffUL);
	seq_put_decimal_ull(m, ' ', task->blocked.sig[0] & 0x7fffffffUL);
	seq_put_decimal_ull(m, ' ', sigign.sig[0] & 0x7fffffffUL);
	seq_put_decimal_ull(m, ' ', sigcatch.sig[0] & 0x7fffffffUL);
	seq_put_decimal_ull(m, ' ', wchan);
	seq_put_decimal_ull(m, ' ', 0);
	seq_put_decimal_ull(m, ' ', 0);
	seq_put_decimal_ll(m, ' ', task->exit_signal);
	seq_put_decimal_ll(m, ' ', task_cpu(task));
	seq_put_decimal_ull(m, ' ', task->rt_priority);
	seq_put_decimal_ull(m, ' ', task->policy);
	seq_put_decimal_ull(m, ' ', delayacct_blkio_ticks(task));
	seq_put_decimal_ull(m, ' ', cputime_to_clock_t(gtime));
	seq_put_decimal_ll(m, ' ', cputime_to_clock_t(cgtime));

	if (mm && permitted) {
		seq_put_decimal_ull(m, ' ', mm->start_data);
		seq_put_decimal_ull(m, ' ', mm->end_data);
		seq_put_decimal_ull(m, ' ', mm->start_brk);
		seq_put_decimal_ull(m, ' ', mm->arg_start);
		seq_put_decimal_ull(m, ' ', mm->arg_end);
		seq_put_decimal_ull(m, ' ', mm->env_start);
		seq_put_decimal_ull(m, ' ', mm->env_end);
	} else
		seq_printf(m, " 0 0 0 0 0 0 0");

	if (permitted)
		seq_put_decimal_ll(m, ' ', task->exit_code);
	else
		seq_put_decimal_ll(m, ' ', 0);

	seq_putc(m, '\n');
	if (mm)
		mmput(mm);

pid_nr_ns()では、namespaceで管理しているpidに変換する関数のようです。namespaceも、不勉強なため説明できるような理解はできていません。

ぼちぼち調べながら見ていきましょう... schedulerが参照するようなcpu timeとかあるので、ここだけ見ても意味は理解できない雰囲気ですね。


format vars description In the kernel space
%dpidプロセスIDpid_nr_ns(pid, ns)
%stcommCOMM名(task_struct*)->comm
%cstate状態フラグのうち最も若いフラグの立っているところ([RSDTtXz]:左が若い)get_task_state()の1文字目
(ll)ppid親プロセスIDtask_tgid_nr_ns(task->real_parent, ns)
(ll)pgidプロセスグループIDtask_pgrp_nr_ns(task, ns)
(ll)sidグループリーダID(?)task_session_nr_ns(task, ns), task->group_leaderのpid
(ll)tty_nrttyに属していれば、tty drviceのidを入れる.なければ0((minor & ~0xFF)<<12) or (major << 8) or ((minor&0xFF)
(ll)tty_pgrpttyのプロセスグループ-
(ull)task->flagsプロセス状態を示すビットマップ(後述)-
(ull)min_fltminor faultカウントの総和(struct task_struct*)->min_flt
(ull)cmin_fltsignal.minor faultカウントの総和(struct signal_struct)->cmin_flt
(ull)maj_fltmajor faultカウントの総和(struct task_struct*)->maj_flt
(ull)cmaj_fltsignal.major faultカウントの総和(struct signal_struct)->cmaj_flt
(ull)utimeユーザランドに費やしたcpu timecputime_to_clock_t(utime)
(ull)stimeシステムに費やしたcpu timecputime_to_clock_t(stime)
(ll)cutime亡くなった子プロセスを含めたユーザランドに費やしたcpu timecputime_to_clock_t((struct signal_struct)->cutime)
(ll)cstime亡くなった子プロセスを含めたシステムに費やしたcpu timecputime_to_clock_t((struct signal_struct)->cstime)
(ll)prioritytask priority()(->prio - 100)
(ll)nicenice値(->static_prio - 120(DEFAULT_PRIO))
(ll)num_threadsスレッド数tsk->signal->nr_threads
(ull)0 (fixed.)--
(ull)start_timeboot based time in ticks, copy_proces()された時nsec_to_clock_t(task->real_start_time)
(ull)vsizeマップされたサイズ[Bytes]PAGE_SIZE * mm->total_vm
(ull)mm->rss_stat.count[ MM_FILEPAGES,MM_ANONPAGES]の和プロセスに割り当てられている物理ページ数(mm ? get_mm_rss(mm) : 0)
(ull)rsslimmax resident set sizesig->rlim[RLIMIT_RSS].rlim_cur
(ull)このプロセスのコード領域の先頭アドレスmm ? (permitted ? mm->start_code : 1) : 0
(ull)このプロセスのコード領域の終了アドレスmm ? (permitted ? mm->end_code : 1) : 0
(ull)スタックアドレスの先頭(スタックの底?)(permitted && mm) ? mm->start_stack : 0
(ull)espstack pointer-(arch依存)
(ull)eipindex pointer?-(arch依存)
(ull)ペンディングされているシグナルのビットマップtask->pending.signal.sig[0] & 0x7fffffffUL
(ull)(確認中)task->blocked.sig[0] & 0x7fffffffUL
(ull)(確認中)sigign.sig[0] & 0x7fffffffUL
(ull)(確認中)sigcatch.sig[0] & 0x7fffffffUL
(ull)wchan待ち状態に入った関数名(名前なし設定だとアドレス)get_wchan(task)
(ull)0 (fixed)--
(ull)0 (fixed)--
(ll)task->exit_signal(確認中)-
(ll)task_cpu(task)(確認中)-
(ull)task->rt_priority(確認中)-
(ull)task->policy(確認中)-
(ull)delayacct_blkio_ticks(task)(確認中)-
(ull)cputime_to_clock_t(gtime)(確認中)-
(ll)cputime_to_clock_t(cgtime)(確認中)-
(ull)mm->start_data(確認中)-
(ull)mm->end_data(確認中)-
(ull)mm->start_brk(確認中)-
(ull)mm->arg_start(確認中)-
(ull)mm->arg_end(確認中)-
(ull)mm->env_start(確認中)-
(ull)mm->env_end(確認中)-
(ll)task->exit_code0 if !permitted(確認中)

コメントも面白いですね。RT-signalは statusを見ろって書いてます。

	/* The signal information here is obsolete.
	 * It must be decimal for Linux 2.0 compatibility.
	 * Use /proc/#/status for real-time signals.
	 */

※per proces flags

/*
 * Per process flags
 */
#define PF_EXITING	0x00000004	/* getting shut down */
#define PF_EXITPIDONE	0x00000008	/* pi exit done on shut down */
#define PF_VCPU		0x00000010	/* I'm a virtual CPU */
#define PF_WQ_WORKER	0x00000020	/* I'm a workqueue worker */
#define PF_FORKNOEXEC	0x00000040	/* forked but didn't exec */
#define PF_MCE_PROCESS  0x00000080      /* process policy on mce errors */
#define PF_SUPERPRIV	0x00000100	/* used super-user privileges */
#define PF_DUMPCORE	0x00000200	/* dumped core */
#define PF_SIGNALED	0x00000400	/* killed by a signal */
#define PF_MEMALLOC	0x00000800	/* Allocating memory */
#define PF_NPROC_EXCEEDED 0x00001000	/* set_user noticed that RLIMIT_NPROC was exceeded */
#define PF_USED_MATH	0x00002000	/* if unset the fpu must be initialized before use */
#define PF_USED_ASYNC	0x00004000	/* used async_schedule*(), used by module init */
#define PF_NOFREEZE	0x00008000	/* this thread should not be frozen */
#define PF_FROZEN	0x00010000	/* frozen for system suspend */
#define PF_FSTRANS	0x00020000	/* inside a filesystem transaction */
#define PF_KSWAPD	0x00040000	/* I am kswapd */
#define PF_MEMALLOC_NOIO 0x00080000	/* Allocating memory without IO involved */
#define PF_LESS_THROTTLE 0x00100000	/* Throttle me less: I clean memory */
#define PF_KTHREAD	0x00200000	/* I am a kernel thread */
#define PF_RANDOMIZE	0x00400000	/* randomize virtual address space */
#define PF_SWAPWRITE	0x00800000	/* Allowed to write to swap */
#define PF_NO_SETAFFINITY 0x04000000	/* Userland is not allowed to meddle with cpus_allowed */
#define PF_MCE_EARLY    0x08000000      /* Early kill for mce process policy */
#define PF_MUTEX_TESTER	0x20000000	/* Thread belongs to the rt mutex tester */
#define PF_FREEZER_SKIP	0x40000000	/* Freezer should not count it as freezable */
#define PF_SUSPEND_TASK 0x80000000      /* this thread called freeze_processes and should not be frozen */



cmin_fltとかの説明。統計情報ですね...

		/*
		 * The resource counters for the group leader are in its
		 * own task_struct.  Those for dead threads in the group
		 * are in its signal_struct, as are those for the child
		 * processes it has previously reaped.  All these
		 * accumulate in the parent's signal_struct c* fields.
		 *
		 * We don't bother to take a lock here to protect these
		 * p->signal fields because the whole thread group is dead
		 * and nobody can change them.
		 *
		 * psig->stats_lock also protects us from our sub-theads
		 * which can reap other children at the same time. Until
		 * we change k_getrusage()-like users to rely on this lock
		 * we have to take ->siglock as well.
		 *
		 * We use thread_group_cputime_adjusted() to get times for
		 * the thread group, which consolidates times for all threads
		 * in the group including the group leader.
		 */

prio : RT[0..99], user [100..140], 120がnice=0, user-nice = -20..+19 => 100..139


とりあえずここまで。。。本当はpsコマンドでタスク状態をモニタして、不具合や性能問題の追跡例を書いてみようかと思っていました。それよりも、ユーザランドからプロセスの状態を見る方法を示したほうが応用範囲が広がるだろうと思われます。

一部未確認がありますが、年内には更新を終わらせたいと思います。。。

DPDK Document邦訳

2015/03/09DPDKimport
個人的な邦訳メモです。
誤訳、思い込みも混入している可能性がありますので、あくまでも参考に。
@2015/03/09

5. Ring Library

The ring allows the management of queues. Instead of having a linked list of infinite size, the rte_ring has the following properties:
  • FIFO
  • Maximum size is fixed, the pointers are stored in a table
  • Lockless implementation
  • Multi-consumer or single-consumer dequeue
  • Multi-producer or single-producer enqueue
  • Bulk dequeue - Dequeues the specified count of objects if successful; otherwise fails
  • Bulk enqueue - Enqueues the specified count of objects if successful; otherwise fails
  • Burst dequeue - Dequeue the maximum available objects if the specified count cannot be fulfilled
  • Burst enqueue - Enqueue the maximum available objects if the specified count cannot be fulfilled
The advantages of this data structure over a linked list queue are as follows:
  • Faster; only requires a single Compare-And-Swap instruction of sizeof(void *) instead of several double-Compare-And-Swap instructions.
  • Simpler than a full lockless queue.
  • Adapted to bulk enqueue/dequeue operations. As pointers are stored in a table, a dequeue of several objects will not produce as many cache misses as in a linked queue. Also, a bulk dequeue of many objects does not cost more than a dequeue of a simple object.
リンクドリストよりも有意な点は以下のとおり:
  • 早い. 複数の"double-Compare-And-Swap"に代わって、たった1つのsizeof(void*)を"Compare-And-Swap"する命令を必要とする
  • 完全なロックレスキューよりも単純である
  • バルクエンキュー・デキュー操作に対応。ポインタはテーブルに保存されるので、幾らかのオブジェクトのデキューでも、リンクドキューのように多くのキャッシュミスは発生しない。多くのオブジェクトのバルクデキューも、単純なオブジェクトのデキューよりもコストはかからない。
The disadvantages:
  • Size is fixed
  • Having many rings costs more in terms of memory than a linked list queue. An empty ring contains at least N pointers.
不利な点は以下のとおり:
  • サイズは固定
  • リンクドリストキューよりも多くのメモリを必要とする。空のリングは少なくともN個のポインタから構成される。
A simplified representation of a Ring is shown in with consumer and producer head and tail pointers to objects stored in the data structure.
(図はちょっと間違っている気がする)

The following code was added in FreeBSD 8.0, and is used in some network device drivers (at least in Intel drivers):

bufring.h in FreeBSD
bufring.c in FreeBSD

The following is a link describing the Linux Lockless Ring Buffer Design.
http://lwn.net/Articles/340400/

5.3. Additional Features

5.3.1. Name
A ring is identified by a unique name.
It is not possible to create two rings with the same name (rte_ring_create() returns NULL if this is attempted).
リングはユニークな名前で識別される。
同じ名前でリングを2つ作成することはできない。これが適用された場合、rte_ring_create() でNULLが返ってくる。
5.3.2. Water Marking
The ring can have a high water mark (threshold).
Once an enqueue operation reaches the high water mark, the producer is notified, if the water mark is configured.
This mechanism can be used, for example, to exert a back pressure on I/O to inform the LAN to PAUSE.
リングはhigh-water-markを持つことができる。
water markが設定されていれば、最初にエンキュー操作がhigh-water-markに達したなら、プロデューサが通知する。
このメカニズムは、例えば、I/Oのバックプレッシャとして、LANにPAUSE要求するために、使える。
5.3.3. Debug
When debug is enabled (CONFIG_RTE_LIBRTE_RING_DEBUG is set), the library stores some per-ring statistic counters about the number of enqueues/dequeues.
These statistics are per-core to avoid concurrent accesses or atomic operations.
デバッグ有効時(CONFIG_RTE_LIBRTE_RING_DEBUG)、ライブラリはリング毎のエンキュー/デキュー統計カウンタを保存する。
これらの統計情報は、コアごとの同時アクセスやアトミックオペレーションを除外する。

5.4. Use Cases

Use cases for the Ring library include:
  • Communication between applications in the DPDK
  • Used by memory pool allocator
リングライブラリのユースケースは以下を含む
  • DPDK内のアプリケーション間のコミュニケーション
  • メモリプールアロケータでの使われ方

5.5. Anatomy of a Ring Buffer (リングバッファの解剖学)

This section explains how a ring buffer operates.
The ring structure is composed of two head and tail couples; one is used by producers and one is used by the consumers.
The figures of the following sections refer to them as prod_head, prod_tail, cons_head and cons_tail.

Each figure represents a simplified state of the ring, which is a circular buffer.
The content of the function local variables is represented on the top of the figure, and the content of ring structure is represented on the bottom of the figure.
リングバッファがどのように動作するかをこのセクションは説明しています。
リング構造は2つのヘッドとテールカップルから構成されている。一つのプロデューサで使用され、1つは、コンシューマによって使用される。
次のセクションの図はprod_head、prod_tail、cons_headとcons_tailとしてそれらを参照してください。

各図はサーキュラバッファであるリングの簡略化された状態を表す。
関数のローカル変数の内容は、図の上に表現され、リング構造のコンテンツは図の下に表されている。
5.5.1. Single Producer Enqueue
5.5.2. Single Consumer Dequeue
5.5.3. Multiple Producers Enqueue
5.5.4. Modulo 32-bit Indexes

5.6. References

  • bufring.h in FreeBSD (version 8)\ http://svn.freebsd.org/viewvc/base/release/8.0.0/sys/sys/buf_ring.h?revision=199625&view=markup
  • bufring.c in FreeBSD (version 8)\ http://svn.freebsd.org/viewvc/base/release/8.0.0/sys/kern/subr_bufring.c?revision=199625&view=markup
  • Linux Lockless Ring Buffer Design\ http://lwn.net/Articles/340400/

7. Mbuf Library

The mbuf library provides the ability to allocate and free buffers (mbufs) that may be used by the DPDK application to store message buffers.
The message buffers are stored in a mempool, using the Mempool Library.

A rte_mbuf struct can carry network packet buffers or generic control buffers (indicated by the CTRL_MBUF_FLAG).
This can be extended to other types.
The rte_mbuf header structure is kept as small as possible and currently uses just two cache lines, with the most frequently used fields being on the first of the two cache lines.
mbufのライブラリは、割り当てるための能力とメッセージバッファを保存するためにDPDKアプリケーションによって使用できる空きバッファ(mbufの)を提供します。
メッセージバッファはMEMPOOLライブラリを使用して、MEMPOOLに保存されます。

rte_mbuf構造体は、ネットワークパケットバッファまたは(CTRL_MBUF_FLAGで示される)一般的なコントロールのバッファを運ぶことができます。
これは、他のタイプに拡張することができる。
rte_mbufヘッダ構造は、可能な限り小さく維持され、現在最も頻繁に使用されるフィールドは、2つのキャッシュ·ラインの最初にあると、ちょうど2つのキャッシュ·ラインを使用している。

7.1. Design of Packet Buffers

7.2. Buffers Stored in Memory Pools

7.3. Constructors

7.4. Allocating and Freeing mbufs

7.5. Manipulating mbufs

This library provides some functions for manipulating the data in a packet mbuf.
For instance:
  • Get data length
  • Get a pointer to the start of data
  • Prepend data before data
  • Append data after data
  • Remove data at the beginning of the buffer (rte_pktmbuf_adj())
  • Remove data at the end of the buffer (rte_pktmbuf_trim()) Refer to the DPDK API Reference for details.
このライブラリは、パケットのmbuf内のデータを操作するためのいくつかの機能を提供します。
  • データ長
  • 最初のデータへのポインタ取得
  • データの前にデータを追加
  • データの後ろにデータを追加
  • バッファの最初のデータを削除 (rte_pktmbuf_adj())
  • バッファの最後のデータを削除 (rte_pktmbuf_trim())
詳細はDPDK API Referenceを参照ください。

7.6. Meta Information

Some information is retrieved by the network driver and stored in an mbuf to make processing easier.
For instance, the VLAN, the RSS hash result (see Poll Mode Driver) and a flag indicating that the checksum was computed by hardware.

An mbuf also contains the input port (where it comes from), and the number of segment mbufs in the chain.

For chained buffers, only the first mbuf of the chain stores this meta information.
一部の情報は、ネットワークドライバにより取得され、より簡単に処理するためのmbufに保存されます。
例えば、VLAN・RSSのハッシュ結果(ポーリングモードドライバを参照)・チェックサムがハードウェアによって計算されたことを示すフラグです。

MBUFも、(それが来た)入力ポート、およびチェーンにおけるセグメントmbufの数が含まれています。
チェーンバッファの場合、チェーンの最初のmbufにのみ、このメタ情報が含まれます。

7.7. Direct and Indirect Buffers

A direct buffer is a buffer that is completely separate and self-contained.
An indirect buffer behaves like a direct buffer but for the fact that the buffer pointer and data offset in it refer to data in another direct buffer.
This is useful in situations where packets need to be duplicated or fragmented, since indirect buffers provide the means to reuse the same packet data across multiple buffers.

A buffer becomes indirect when it is “attached” to a direct buffer using the rte_pktmbuf_attach() function.
Each buffer has a reference counter field and whenever an indirect buffer is attached to the direct buffer, the reference counter on the direct buffer is incremented.
Similarly, whenever the indirect buffer is detached, the reference counter on the direct buffer is decremented.
If the resulting reference counter is equal to 0, the direct buffer is freed since it is no longer in use.
ダイレクトバッファは完全に独立したと自己完結しているバッファです。
間接バッファは、直接バッファのようですが、その中にオフセットバッファポインタとデータが別のダイレクトバッファ内のデータを参照するということのために動作します。
このパケットは、間接バッファは、複数のバッファで同じパケットデータを再利用するための手段を提供するので、複製またはフラグメント化する必要がある状況において有用である。

それはrte_pktmbuf_attach()関数を使用して直接バッファに"attached"されたときにバッファは、間接的になります。
各バッファは、参照カウンタフィールドを有しており、間接バッファを直接バッファに接続されているときはいつでも、直接バッファ上の参照カウンタがインクリメントされる。
間接バッファが切り離されるたびにも同様に、直接バッファ上の参照カウンタをデクリメントする。
得られた参照カウンタが0に等しい場合には使用されなくなったので、ダイレクトバッファーは解放される。
There are a few things to remember when dealing with indirect buffers.
First of all, it is not possible to attach an indirect buffer to another indirect buffer.
Secondly, for a buffer to become indirect, its reference counter must be equal to 1, that is, it must not be already referenced by another indirect buffer.
Finally, it is not possible to reattach an indirect buffer to the direct buffer (unless it is detached first).

While the attach/detach operations can be invoked directly using the recommended rte_pktmbuf_attach() and rte_pktmbuf_detach() functions, it is suggested to use the higher-level rte_pktmbuf_clone() function, which takes care of the correct initialization of an indirect buffer and can clone buffers with multiple segments.

Since indirect buffers are not supposed to actually hold any data, the memory pool for indirect buffers should be configured to indicate the reduced memory consumption.
Examples of the initialization of a memory pool for indirect buffers (as well as use case examples for indirect buffers) can be found in several of the sample applications, for example, the IPv4 Multicast sample application.
間接バッファを扱うときに覚えておくべきことがいくつかあります。
まず第一に、それは他の間接的なバッファに間接バッファをattachすることは不可能である。
第二に、間接的になるためのバッファのために、その参照カウンタはつまり、それは既に別の間接バッファによって参照されてはならない、1に等しくなければなりません。
最後に、それは(それが最初に切り離されていない限り)、直接バッファへ間接バッファを再接続(reattach)することはできません。

デタッチ/アタッチ操作は推奨さrte_pktmbuf_attach()とrte_pktmbuf_detach()関数を使用して直接呼び出すことができますが、
それは間接バッファの正しい初期化の世話をして複製することができ、より高いレベルのrte_pktmbuf_clone()関数を使用することが提案されている。
複数のセグメントを持つバッファも正しく行われるため、rte_pktmbuf_clone()関数の利用が提唱されている。

間接的なバッファが、実際にはデータを保持することを想定していないので、間接バッファ用メモリプールがメモリ消費の減少を示すように構成されるべきである。
間接バッファのメモリプール(および間接的なバッファの使用事例)の初期の例は、例えば、IPv4マルチキャストサンプルアプリケーション、サンプルアプリケーションのいくつかの中に見出すことができる。

7.8. Debug

In debug mode (CONFIG_RTE_MBUF_DEBUG is enabled), the functions of the mbuf library perform sanity checks before any operation (such as, buffer corruption, bad type, and so on).

8. Poll Mode Driver

Run-to-completion scheduling is a scheduling model
in which each task runs until it either finishes, or explicitly yields control back to the scheduler.
Run to completion systems typically have an event queue which is serviced either in strict order of admission by an event loop, or by an admission scheduler which is capable of scheduling events out of order, based on other constraints such as deadlines.


The DPDK includes 1 Gigabit, 10 Gigabit and 40 Gigabit and para virtualized virtio Poll Mode Drivers.
DPDKは1ギガビット、10ギガビットおよび40ギガビットおよびパラ仮想化のvirtioポールモードドライバが含まれています。
A Poll Mode Driver (PMD) consists of APIs, provided through the BSD driver running in user space, to configure the devices and their respective queues.
In addition, a PMD accesses the RX and TX descriptors directly without any interrupts (with the exception of Link Status Change interrupts) to quickly receive, process and deliver packets in the user's application.
This section describes the requirements of the PMDs, their global design principles and proposes a high-level architecture and a generic external API for the Ethernet PMDs.
ポーリングモードドライバ(PMD)は、デバイスとそれぞれのキューを設定するための、ユーザ空間で実行されているBSDのドライバを介して提供されるAPI群で構成されています。
また、PMDはRXおよびTXがすぐに、プロセスを受信して​​、ユーザーのアプリケーションでパケットを配信するために(変更割り込みリンクステータスを除く)すべての割り込みなしで直接ディスクリプタにアクセスします。
このセクションでは、PMDの要件(グローバルな設計原則)について説明し、ハイレベルのアーキテクチャとイ​​ーサネットのPMDのための一般的な外部APIを提案します。

8.1. Requirements and Assumptions(要件と前提)

The DPDK environment for packet processing applications allows for two models, run-to-completion and pipe-line:
  • In the run-to-completion model, a specific port's RX descriptor ring is polled for packets through an API.
Packets are then processed on the same core and placed on a port's TX descriptor ring through an API for transmission.
  • In the pipe-line model, one core polls one or more port's RX descriptor ring through an API.
Packets are received and passed to another core via a ring.
The other core continues to process the packet which then may be placed on a port's TX descriptor ring through an API for transmission.
パケット処理アプリケーションのためのDPDK環境は、run-to-completionとパイプライン、二つのモデルを許可する。
  • run-to-completionモデルでは、特定のポートのRXデスクリプタリングは、APIを介してパケットのためにポーリングされる。\ パケットは、同じコアに処理され、送信のためのAPIを介してポートのTXデスクリプタリング上に配置される。
  • パイプラインモデルでは、一つのコアにポーリングつまたは複数のポートのRXデスクリプタリングAPIを介する。\ パケットは受信され、リングを介して別のコアに渡される。\ 他のコアは、送信のためのAPIを介してポートのTXデスクリプタリング上に配置することができるパケットの処理を続行します。
In a synchronous run-to-completion model, each logical core assigned to the DPDK executes a packet processing loop that includes the following steps:
-Retrieve input packets through the PMD receive API
-Process each received packet one at a time, up to its forwarding
-Send pending output packets through the PMD transmit API
同期実行のrun-to-completionでは、DPDKに割り当てられた各論理コアは、以下のステップを含むパケット処理ループを実行する。
  • PMDの受信APIを介して、入力パケットを取得
  • プロセスごとに、その転送まで、一度に入力パケットを受信
  • PMD送信APIを介して、保留中の出力パケットを送信します
★フロー毎にlcoreを割り当てて, 送り終わるまで一括で処理を流す.
Conversely, in an asynchronous pipe-line model, some logical cores may be dedicated to the retrieval of received packets and other logical cores to the processing of previously received packets.
Received packets are exchanged between logical cores through rings. The loop for packet retrieval includes the following steps:
-Retrieve input packets through the PMD receive API
-Provide received packets to processing lcores through packet queues
逆に、非同期パイプラインのモデルでは、いくつかの論理コアは、以前に受信したパケットの処理と、
受信したパケットと他の論理コアの検索専用にすることができる。
受信したパケットは、リングを通して論理コアの間で交換されている。
パケットの取得のためのループは、以下のステップを含む。
  • PMDの受信APIを介して、入力パケットを取得
  • パケットキューを介して処理lcoresに受信したパケットを提供する
The loop for packet processing includes the following steps:
-Retrieve the received packet from the packet queue
-Process the received packet, up to its retransmission if forwarded
パケット処理ループは、以下のステップを含む。
  • パケットキューから受信したパケットを取得
  • 転送された場合は、その再送信までの受信したパケットを処理する
To avoid any unnecessary interrupt processing overhead, the execution environment must not use any asynchronous notification mechanisms.
Whenever needed and appropriate, asynchronous communication should be introduced as much as possible through the use of rings.
不要な割り込み処理のオーバーヘッドを回避するために、実行環境は、任意の非同期通知メカニズムを使用してはならない。
必要に応じて、適切なときはいつでも、非同期通信は、リングの使用を介して可能な限り導入すべきである。

Avoiding lock contention is a key issue in a multi-core environment.
To address this issue, PMDs are designed to work with per-core private resources as much as possible.
For example, a PMD maintains a separate transmit queue per-core, per-port.
In the same way, every receive queue of a port is assigned to and polled by a single logical core (lcore).
ロック競合を回避することは、マルチコア環境において重要な問題である。
この問題に対処するために、PMDは極力コアごとのプライベートリソースで動作するように設計されている。
例えば、PMDはポート単位、コア単位の独立した送信キューを維持します。
同様に、すべての受信ポートのキューは、割り当てられている1つの論理コア(lcore)によってポーリングされる。
To comply with Non-Uniform Memory Access (NUMA), memory management is designed to assign to each logical core a private buffer pool in local memory to minimize remote memory access.
The configuration of packet buffer pools should take into account the underlying physical memory architecture in terms of DIMMS, channels and ranks.
The application must ensure that appropriate parameters are given at memory pool creation time. See Mempool Library.
非一様メモリアクセス(NUMA)に準拠するために、メモリ管理は、リモートメモリアクセスを最小限に抑えるために、
各論理コアにローカルメモリにプライベートバッファプールを割り当てるように設計されています。
パケットバッファプールの構成は、DIMMを、チャンネルやランクの面で考慮に基礎となる物理メモリアーキテクチャを取る必要があります。
アプリケーションは、適切なパラメータは、メモリプールの作成時に与えられていることを確認する必要があります。
詳しくは MEMPOOLライブラリ を 参照してください。


8.2. Design Principles (設計原則)

The API and architecture of the Ethernet* PMDs are designed with the following guidelines in mind.
イーサネットのPMDのAPIとアーキテクチャは、次のガイドラインに従って設計されています。
PMDs must help global policy-oriented decisions to be enforced at the upper application level.
Conversely, NIC PMD functions should not impede the benefits expected by upper-level global policies, or worse prevent such policies from being applied.
MDは、グローバルなポリシーオリエンテッドの決定が上位のアプリケーションレベルで施行されるようにを支援する必要があります。
逆に、NIC PMD機能は、上位レベルのグローバルポリシーによって期待される利益を妨げたり、
またはそのような適用されるポリシーにより悪化することは、すべきではない。
For instance, both the receive and transmit functions of a PMD have a maximum number of packets/descriptors to poll.
This allows a run-to-completion processing stack to statically fix or to dynamically adapt its overall behavior through different global loop policies, such as:
-Receive, process immediately and transmit packets one at a time in a piecemeal fashion.
-Receive as many packets as possible, then process all received packets, transmitting them immediately.
-Receive a given maximum number of packets, process the received packets, accumulate them and finally send all accumulated packets to transmit.
例えば、PMDの両方の受信および送信機能は、ポーリングするためのパケット/記述子の最大数を持っている。
これは、run-to-completion処理スタックを静的に固定するために、動的などの異なるグローバルループポリシーを通じて、その全体的な動作を適合させることができます。
  • 受信すると、直ちに処理して、断片的に同時にそのパケットを送信する
  • できるだけ多くのパケットを受信し、すべての受信パケットを処理し、その後すぐにそれらを送信する
  • 指定された最大数のパケットを受信、受信したパケットは処理され、それらを蓄積し、最終的に伝送するために、すべての蓄積されたパケットを送信する
To achieve optimal performance,
overall software design choices and pure software optimization techniques must be considered and balanced against available low-level hardware-based optimization features (CPU cache properties, bus speed, NIC PCI bandwidth, and so on).
The case of packet transmission is an example of this software/hardware tradeoff issue when optimizing burst-oriented network packet processing engines.
In the initial case, the PMD could export only an rte_eth_tx_one function to transmit one packet at a time on a given queue.
On top of that, one can easily build an rte_eth_tx_burst function that loops invoking the rte_eth_tx_one function to transmit several packets at a time.
However, an rte_eth_tx_burst function is effectively implemented by the PMD to minimize the driver-level transmit cost per packet through the following optimizations:
最適なパフォーマンス、全体的なソフトウェアの設計選択と純粋なソフトウェア最適化手法を実現するために検討し、利用可能な低レベルのハードウェアベースの最適化機能(CPUキャッシュプロパティ、バス速度、NIC PCI帯域幅など)に対してバランスされなければならない。
バースト指向のネットワークパケット処理エンジンを最適化する際のパケット送信のケースは、このソフトウェア/ハードウェアのトレードオフの問題の一例である。
最初のケースで、PMDは、指定されたキュー上で一度に一つのパケットを送信するために、rte_eth_tx_one関数をエクスポートできます。
その上で、簡単に一度に複数のパケットを送信する、rte_eth_tx_one()関数を繰り返し呼び出すrte_eth_tx_burst機能を構築することができます。
しかし、rte_eth_tx_burst()関数は、PMDで以下の最適化によって、パケットごとにかかるドライバレベルの送信コストを最小限に抑えるよう、効果的に実装されます。
-Share among multiple packets the un-amortized cost of invoking the rte_eth_tx_one function.
-Enable the rte_eth_tx_burst function to take advantage of burst-oriented hardware features (prefetch data in cache, use of NIC head/tail registers)
to minimize the number of CPU cycles per packet,
for example by avoiding unnecessary read memory accesses to ring transmit descriptors,
or by systematically using arrays of pointers that exactly fit cache line boundaries and sizes.
-Apply burst-oriented software optimization techniques to remove operations that would otherwise be unavoidable, such as ring index wrap back management.
  • 複数のパケットのうち、rte_eth_tx_one関数を呼び出すの未償却原価(呼び出しオーバヘッド?)
  • rte_eth_tx_burst関数を有効にする。\パケットあたりのCPUサイクル数を最小化するため、バースト志向ハードウェア機能(キャッシュ内のプリフェッチデータや、NICヘッド/テールレジスタを使う)\たとえば、リング送信デスクリプタのメモリ読み込みアクセスを除去したり、キャッシュライン境界及びサイズに正確に適合(アライメント)したポインタアレイを使う。
  • このようなリングインデックスバックラップ管理などの他の方法で避けられないだろうな操作を削除するには、バースト指向ソフトウェア最適化技術を適用します。
Burst-oriented functions are also introduced via the API for services that are intensively used by the PMD.
This applies in particular to buffer allocators used to populate NIC rings, which provide functions to allocate/free several buffers at a time.
For example, an mbuf_multiple_alloc function returning an array of pointers to rte_mbuf buffers which speeds up the receive poll function of the PMD when replenishing multiple descriptors of the receive ring.
バースト指向の関数も集中的にPMDで使用されるサービスについては、APIを介して導入されている。
これは一度にいくつかのバッファを割り当てる/開放するための機能を提供する、NICリングを取り込むために使用されるバッファアロケータが特に当てはまる。
例えば、ポインタの配列を返すmbuf_multiple_alloc機能は、受信リングの複数の記述子を補充する際にPMDの受信ポーリング機能を高速化するバッファをrte_mbufする。

8.3. Logical Cores, Memory and NIC Queues Relationships (論理コア、メモリ、NICキューの関係)¶

The DPDK supports NUMA allowing for better performance when a processor's logical cores and interfaces utilize its local memory.
Therefore, mbuf allocation associated with local PCIe* interfaces should be allocated from memory pools created in the local memory.
The buffers should, if possible, remain on the local processor to obtain the best performance results and RX and TX buffer descriptors should be populated with mbufs allocated from a mempool allocated from local memory.

The run-to-completion model also performs better if packet or data manipulation is in local memory instead of a remote processors memory.
This is also true for the pipe-line model provided all logical cores used are located on the same processor.

Multiple logical cores should never share receive or transmit queues for interfaces since this would require global locks and hinder performance.
DPDKは、プロセッサの論理コアとインタフェースは、そのローカルメモリを利用するときに、より良いパフォーマンスを可能にするNUMAをサポートしています。

したがって、ローカルのPCIe*インターフェイスに関連付けられたmbufの割り当ては、ローカルメモリ内に作成されたメモリプールから割り当てられるべきである。
バッファは、(可能な場合は)最高のパフォーマンス結果を得るために、ローカルプロセッサ上に残すべきであり、
RXおよびTXバッファデスクリプタは、ローカルメモリから割り当てられたmempoolからから割り当てられたのmbufに補充(populate)する必要があります。

run-to-completionモデルもまた、パケットまたはデータが、リモートプロセッサメモリの代わりにローカルメモリ内で操作されるならば、パフォーマンスが良くなります。
これはまた、同じプロセッサ上に配置され使用されるすべての論理コアを提供し、パイプラインモデルについても同様です。

複数の論理コアは、これはグローバルロックが必要ですし、パフォーマンスを妨げるので、インターフェイスの受信キュー又は送信キューを共有することはありません。

8.4. Device Identification and Configuration (デバイスの識別と設定)

8.4.1. Device Identification(識別)
Each NIC port is uniquely designated by its (bus/bridge, device, function) PCI identifiers assigned by the PCI probing/enumeration function executed at DPDK initialization.
Based on their PCI identifier, NIC ports are assigned two other identifiers:
- A port index used to designate the NIC port in all functions exported by the PMD API.
- A port name used to designate the port in console messages, for administration or debugging purposes. For ease of use, the port name includes the port index.
各NICポートを一意にプロービングPCI / DPDKの初期化時に実行される列挙関数によって割り当てられたその(バス/ブリッジ、デバイス、機能)PCI識別子によって指定されている。
これらのPCI識別子に基づいて、NICポートに2つの他の識別子を割り当てられています。
  • PMD APIによってエクスポートされたすべての関数でNICポートを指定するためのポートインデックス
  • 管理またはデバッグの目的のために、コンソールメッセージでポートを指定するために使用されるポート名。使いやすさのために、ポート名、ポートインデックスを含む。

8.4.2. Device Configuration(設定)
The configuration of each NIC port includes the following operations:
- Allocate PCI resources
- Reset the hardware (issue a Global Reset) to a well-known default state
- Set up the PHY and the link
- Initialize statistics counters
The PMD API must also export functions to start/stop the all-multicast feature of a port and functions to set/unset the port in promiscuous mode.
各NICポートの設定には、次の操作が含まれます。
  • PCIリソースを割り当て
  • よく知られているデフォルトの状態に(グローバルリセットを発行して)ハードウェアをリセット
  • PHYとリンクを設定
  • 統計カウンタを初期化
PMD APIは、プロミスキャスモードのポートに設定/解除するための機能と、ポートのall-multicast機能を開始/停止するための関数を提供する必要があります。
Some hardware offload features must be individually configured at port initialization through specific configuration parameters.
This is the case for the Receive Side Scaling (RSS) and Data Center Bridging (DCB) features for example.
一部のハードウェアオフロード機能は、個別に具体的な設定パラメータを介してポート初期化時に設定する必要があります。
これは、例えば、受信側スケーリング(RSS)やデータセンターブリッジング(DCB)機能のケースがあります。

8.4.3. On-the-Fly Configuration
All device features that can be started or stopped “on the fly” (that is, without stopping the device) do not require the PMD API to export dedicated functions for this purpose.

All that is required is the mapping address of the device PCI registers to implement the configuration of these features in specific functions outside of the drivers.

For this purpose, the PMD API exports a function that provides all the information associated with a device that can be used to set up a given device feature outside of the driver.
This includes the PCI vendor identifier, the PCI device identifier, the mapping address of the PCI device registers, and the name of the driver.

The main advantage of this approach is that it gives complete freedom on the choice of the API used to configure, to start, and to stop such features.
"オンザフライ"で開始や停止ができる、すべてのデバイス機能(つまり、デバイスを停止せずにできるもの)は、この目的のために専用の関数をエクスポートするPMD APIを必要しません。

要求されているのは、デバイスのPCIのマッピングアドレスはドライバーの外の特定の機能でこれらの機能の構成を実装するために登録している。

この目的のために、PMD APIはドライバの外側に所定のデバイスの機能を設定するために使用することができるデバイスに関連付けられたすべての情報を提供する関数をエクスポートする。
これは、PCIベンダ識別子、PCIデバイスID、PCIデバイスのレジスタのマッピングアドレス、ドライバの名前を含む。

このアプローチの主な利点は、構成を開始するために、そのような機能を停止するために使用されるAPIの選択に完全な自由を与えることである。
As an example, refer to the configuration of the IEEE1588 feature for the Intel® 82576 Gigabit Ethernet Controller and the Intel® 82599 10 Gigabit Ethernet Controller controllers in the testpmd application.

Other features such as the L3/L4 5-Tuple packet filtering feature of a port can be configured in the same way.
Ethernet* flow control (pause frame) can be configured on the individual port.
Refer to the testpmd source code for details.
Also, L4 (UDP/TCP/ SCTP) checksum offload by the NIC can be enabled for an individual packet as long as the packet mbuf is set up correctly.
In terms of UDP tunneling packet, the PKT_TX_UDP_TUNNEL_PKT flag must be set to enable tunneling packet TX checksum offload for both outer layer and inner layer.
Refer to the testpmd source code (specifically the csumonly.c file) for details.

That being said, the support of some offload features implies the addition of dedicated status bit(s) and value field(s) into the rte_mbuf data structure, along with their appropriate handling by the receive/transmit functions exported by each PMD.

For instance, this is the case for the IEEE1588 packet timestamp mechanism, the VLAN tagging and the IP checksum computation, as described in the Section 7.6 “Meta Information”.
例として、インテル®82576ギガビット·イーサネット·コントローラとtestpmdアプリケーションにおけるインテル®82599 10ギガビットイーサネットコントローラコントローラーのIEEE1588機能の設定を参照してください。

そのようなポートのL3 / L4の5タプルのパケットフィルタリング機能などの他の機能も同様に構成することができる。
イーサネットフロー制御(ポーズフレーム)は、個々のポートに設定できます。
詳細についてはtestpmdソースコードを参照してください。
また、NICによってL4(UDP / TCP / SCTP)チェックサムオフロードは、ロングパケットのmbufが正しく設定されているように、個々のパケットのために有効にすることができます。
UDPトンネルパケットの面では、PKT_TX_UDP_TUNNEL_PKTフラグは外層と内層の両方のトンネリングパケットTXチェックサムオフロードを有効にするために設定する必要があります。
詳細については、testpmdソースコード(特にcsumonly.cファイル)を参照してください。

それは言われている、いくつかのオフロード機能がサポートは、各PMDによってエクスポートされた送信/受信機能によってその適切な取り扱いとともに、
rte_mbufデータ構造に専用のステータスビット(単数または複数)および値フィールド(単数または複数)の付加を意味する。

7.6項「メタ情報」に記載されているように例えば、これは、IEEE1588パケットのタイムスタンプのメカニズム、VLANタギングおよびIPチェックサム計算のためのケースです。


8.4.4. Configuration of Transmit and Receive Queues
Each transmit queue is independently configured with the following information:
各送信キューは、独立して、以下の情報で構成されている:
  • The number of descriptors of the transmit ring
  • The socket identifier used to identify the appropriate DMA memory zone from which to allocate the transmit ring in NUMA architectures
  • The values of the Prefetch, Host and Write-Back threshold registers of the transmit queue
  • The minimum transmit packets to free threshold (tx_free_thresh).
When the number of descriptors used to transmit packets exceeds this threshold, the network adaptor should be checked to see if it has written back descriptors.
A value of 0 can be passed during the TX queue configuration to indicate the default value should be used.
The default value for tx_free_thresh is 32.
This ensures that the PMD does not search for completed descriptors until at least 32 have been processed by the NIC for this queue.
  • The minimum RS bit threshold. The minimum number of transmit descriptors to use before setting the Report Status (RS) bit in the transmit descriptor. Note that this parameter may only be valid for Intel 10 GbE network adapters. The RS bit is set on the last descriptor used to transmit a packet if the number of descriptors used since the last RS bit setting, up to the first descriptor used to transmit the packet, exceeds the transmit RS bit threshold (tx_rs_thresh). In short, this parameter controls which transmit descriptors are written back to host memory by the network adapter. A value of 0 can be passed during the TX queue configuration to indicate that the default value should be used. The default value for tx_rs_thresh is 32. This ensures that at least 32 descriptors are used before the network adapter writes back the most recently used descriptor. This saves upstream PCIe* bandwidth resulting from TX descriptor write-backs. It is important to note that the TX Write-back threshold (TX wthresh) should be set to 0 when tx_rs_thresh is greater than 1. Refer to the Intel® 82599 10 Gigabit Ethernet Controller Datasheet for more details.
  • 送信リングのデスクリプタの数
  • NUMAアーキテクチャで送信リングを割り当てるのに適切なDMAメモリ領域を識別するために使用されるソケット識別子
  • プリフェッチ、ホスト、送信キューのライトバックしきい値レジスタの値
  • 送信パケットを開放するための最小のしきい値(tx_free_thresh)。\ パケットを送信するために使用されるデスクリプタの数が、このしきい値を超えたときに、ネットワークアダプタは、ディスクリプタをライトバックしているかどうかがチェックされるべきである。
0の値は、デフォルト値が使用されるべきであることを示すため、TXキューの構成中に渡すことができる。
tx_free_threshのデフォルト値は32です。\ これは、PMDが、少なくとも32個のデスクリプタがこのキューにNICによって処理されるまで、完了したデスクリプタを検索しないことを確実にする。
  • 最小 RSビットの閾値。送信ディスクリプタにレポートステータス(RS)ビットを設定する前に、使用する送信ディスクリプタの最小数。 このパラメータは、インテルの10 GbEネットワークアダプターに対して有効であることに注意してください。 最後のRSビットの設定から使用さ記述子の数は、パケットを送信するために使用される第1の記述子まで、送信RSビット閾値(tx_rs_thresh)を超える。RSビットは、パケットを送信するために使用される最後の記述子に設定されている 要するに、記述子を伝送するこのパラメータを制御し、ネットワークアダプタによってホストメモリに書き戻される。 0の値は、デフォルト値が使用されるべきであることを示すために、TXキューを構成中に通過させることができる。 tx_rs_threshのデフォルト値は、このネットワークアダプタは、最も最近使用された記述子を書き戻す前に、少なくとも32の記述子が使用されることを保証する32である。 これは、TXディスクリプタライトバックから生じる上流のPCIe *帯域幅を節約できます。 それはtx_rs_threshは詳細についてはインテル®82599 10ギガビットイーサネットコントローラデータシートを参照してください1よりも大きい場合、TXライトバックしきい値(TXのwthresh)が0に設定されるべきことに注意することが重要です。
The following constraints must be satisfied for tx_free_thresh and tx_rs_thresh:
- tx_rs_thresh must be greater than 0.
- tx_rs_thresh must be less than the size of the ring minus 2.
- tx_rs_thresh must be less than or equal to tx_free_thresh.
- tx_free_thresh must be greater than 0.
- tx_free_thresh must be less than the size of the ring minus 3.
- For optimal performance, TX wthresh should be set to 0 when tx_rs_thresh is greater than 1.
One descriptor in the TX ring is used as a sentinel to avoid a hardware race condition, hence the maximum threshold constraints.
以下の制約がtx_free_thresh と tx_rs_thresh とで満たさなければなりません。
  • tx_rs_threshは 0より大きくなければなりません。
  • tx_rs_threshは リングのサイズ-2 よりも 小さくなければなりません。
  • tx_rs_threshは tx_free_thresh以下でなければなりません。
  • tx_free_threshは 0より大きくなければなりません。
  • tx_free_threshは リングのサイズ -3 よりも 小さくなければなりません。
  • tx_rs_threshが 1より大きい場合に最適なパフォーマンスを得るには、TX wthreshは0に設定する必要があります。
TXリング内の1つのデスクリプタは、最大閾値制約にしたがって、ハードウェアの競合状態を回避するために、番人(sentinel)として使用される。
Note When configuring for DCB operation, at port initialization, both the number of transmit queues and the number of receive queues must be set to 128.
注意:DCB操作に構成する場合は、ポートの初期化時に、送信キューの数と受信キューの数の両方を128に設定する必要があります。


8.5. Poll Mode Driver API¶

8.5.1. Generalities¶
By default, all functions exported by a PMD are lock-free functions that are assumed not to be invoked in parallel on different logical cores to work on the same target object.
For instance, a PMD receive function cannot be invoked in parallel on two logical cores to poll the same RX queue of the same port.
Of course, this function can be invoked in parallel by different logical cores on different RX queues.
It is the responsibility of the upper-level application to enforce this rule.
デフォルトでは、PMDによってエクスポートすべての機能は、同じターゲットオブジェクト上で動作するように別の論理コア上で並列に呼び出されていないものとされているロックフリー機能である。
例えば、PMDの受信関数が、同じポートの同じRXキューをポーリングするために、2つの論理コア上で並行して呼び出すことはできません。
もちろん、この機能は、異なるRXキューの異なる論理コアで並列に呼び出すことができる。それは、この規則を施行する上位アプリケーションの責任です。
If needed, parallel accesses by multiple logical cores to shared queues can be explicitly protected by dedicated inline lock-aware functions built on top of their corresponding lock-free functions of the PMD API.
必要に応じて、共有キューに複数の論理コアによる並列アクセスは、明示的にPMDのAPIのそれらの対応するロックフリー機能の上に構築された専用のインラインロック認識機能によって保護することができる。

8.5.2. Generic Packet Representation (一般論)
A packet is represented by an rte_mbuf structure, which is a generic metadata structure containing all necessary housekeeping information.
This includes fields and status bits corresponding to offload hardware features, such as checksum computation of IP headers or VLAN tags.
パケットは、すべての必要なハウスキーピング情報を含む一般的なメタデータ構造であるrte_mbuf構造によって表される。
これは、フィールドや、IPヘッダやVLANタグのチェックサム計算のようなハードウェア機能をオフロードするために、対応するステータスビットを含む。
The rte_mbuf data structure includes specific fields to represent, in a generic way, the offload features provided by network controllers.
For an input packet, most fields of the rte_mbuf structure are filled in by the PMD receive function with the information contained in the receive descriptor.
Conversely, for output packets, most fields of rte_mbuf structures are used by the PMD transmit function to initialize transmit descriptors.

The mbuf structure is fully described in the Mbuf Library chapter.
rte_mbufデータ構造は、一般的な方法で、ネットワークコントローラによって提供されるオフロード機能を表すために特定のフィールドを含む。
入力されたパケットの場合は、rte_mbuf構造のほとんどのフィールドは、受信ディスクリプタに含まれる情報と受信機能がPMDによって記入されている。
逆に、出力パケットのために、rte_mbuf構造体のほとんどのフィールドは、送信ディスクリプタを初期化する機能を送信PMDによって使用されます。

mbuf構造は、mbufライブラリの章で完全に記載されている。

8.5.3. Ethernet Device API
The Ethernet device API exported by the Ethernet PMDs is described in the DPDK API Reference.
イーサネットのPMDによってエクスポートされた、イーサネットデバイスAPIは、DPDK APIリファレンスで説明されています。

8.6. Vector PMD for IXGBE

Vector PMD uses Intel® SIMD instructions to optimize packet I/O.
It improves load/store bandwidth efficiency of L1 data cache by using a wider SSE/AVX register 1 (1).
The wider register gives space to hold multiple packet buffers so as to save instruction number when processing bulk of packets.
ベクターPMDは、パケットI/Oを最適化するために、Intel(R)のSIMD命令を使用しています。
より広いSSE/AVXレジスタ1(1)を用いて、L1データキャッシュのロード/ストアの帯域幅効率を向上させる。
広いレジスタは、パケットの大部分を処理するときに命令数を抑えるために、複数のパケットバッファを保持するためのスペースを提供します。
There is no change to PMD API.
The RX/TX handler are the only two entries for vPMD packet I/O.
They are transparently registered at runtime RX/TX execution if all condition checks pass.
PMD APIへの変更はありません。RX/TXハンドラは vPMDパケットI/Oのための唯一つのエントリです。
すべての条件チェックに合格した場合、それらはRX/TX実行のランタイムに透過的に登録されてます。
1. To date, only an SSE version of IX GBE vPMD is available.
To ensure that vPMD is in the binary code, ensure that the option CONFIG_RTE_IXGBE_INC_VECTOR=y is in the configure file.
現在までに、IX GBE vPMDの唯一のSSEバージョンが利用可能です。
vPMDはバイナリコードであることを保証するために、オプションの"CONFIG_RTE_IXGBE_INC_VECTOR = y"が構成ファイル内にあることを確認してください。
Some constraints apply as pre-conditions for specific optimizations on bulk packet transfers.
The following sections explain RX and TX constraints in the vPMD.
いくつかの制約は、バルクパケット転送に特定の最適化のための前提条件として適用されます。
以下のセクションでは、vPMDでRXおよびTXの制約について説明します。

8.6.1. RX Constraints
8.6.1.1. Prerequisites and Pre-conditions
The following prerequisites apply:
- To enable vPMD to work for RX, bulk allocation for Rx must be allowed.
- The RTE_LIBRTE_IXGBE_RX_ALLOW_BULK_ALLOC=y configuration MACRO must be set before compiling the code.
次の前提条件が適用されます:
  • RXのためにvPMDを有効にするには、Rxのためのバルク割り当てが許可されなければならない。
  • RTE_LIBRTE_IXGBE_RX_ALLOW_BULK_ALLOC = Yの構成マクロは、コードをコンパイルする前に設定する必要があります。
Ensure that the following pre-conditions are satisfied:
- rxq->rx_free_thresh >= RTE_PMD_IXGBE_RX_MAX_BURST
- rxq->rx_free_thresh < rxq->nb_rx_desc
- (rxq->nb_rx_desc % rxq->rx_free_thresh) == 0
- rxq->nb_rx_desc < (IXGBE_MAX_RING_DESC - RTE_PMD_IXGBE_RX_MAX_BURST)
These conditions are checked in the code.

Scattered packets are not supported in this mode.
If an incoming packet is greater than the maximum acceptable length of one “mbuf” data size (by default, the size is 2 KB), vPMD for RX would be disabled.

By default, IXGBE_MAX_RING_DESC is set to 4096 and RTE_PMD_IXGBE_RX_MAX_BURST is set to 32.
以下の前提条件が満たされていることを確認します:
  • rxq-> rx_free_thresh> = RTE_PMD_IXGBE_RX_MAX_BURST
  • rxq-> rx_free_thresh nb_rx_desc
  • (rxq-> nb_rx_desc%rxq-> rx_free_thresh)== 0
  • rxq-> nb_rx_desc <(IXGBE_MAX_RING_DESC - RTE_PMD_IXGBE_RX_MAX_BURST)
これらの条件は、コード内でチェックされます。

スカターパケットは、このモードではサポートされません。
入力パケットは、一つの"buf"データサイズの最大許容長さよりも大きい場合(デフォルトでは、サイズが2 KBです)、RX用vPMDを無効にすることでしょう。

デフォルトでは、IXGBE_MAX_RING_DESCは4096に設定され、RTE_PMD_IXGBE_RX_MAX_BURSTは32に設定されている。


8.6.1.2. Feature not Supported by RX Vector PMD
Some features are not supported when trying to increase the throughput in vPMD.
They are:
- IEEE1588
- FDIR
- Header split
- RX checksum off load
vPMDでスループットを向上させるためにしようとしたときに一部の機能がサポートされていません。それらはです:
- IEEE1588
- FDIR
- Header split
- RX checksum off load

Other features are supported using optional MACRO configuration. They include:
- HW VLAN strip
- HW extend dual VLAN
- Enabled by RX_OLFLAGS (RTE_IXGBE_RX_OLFLAGS_DISABLE=n)
その他の機能は、オプションのマクロ設定を使用してサポートされます。 それらは次のとおりです。
- HW VLAN strip
- HW extend dual VLAN
- Enabled by RX_OLFLAGS (RTE_IXGBE_RX_OLFLAGS_DISABLE=n)

To guarantee the constraint, configuration flags in dev_conf.rxmode will be checked:
- hw_vlan_strip
- hw_vlan_extend
- hw_ip_checksum
- header_split
- dev_conf
fdir_conf->mode will also be checked.
制約を保証するために、dev_conf.rxmode内の設定フラグがチェックされます。
- hw_vlan_strip
- hw_vlan_extend
- hw_ip_checksum
- header_split
- dev_conf



8.6.1.3. RX Burst Size
As vPMD is focused on high throughput, it assumes that the RX burst size is equal to or greater than 32 per burst.
It returns zero if using nb_pkt < 32 as the expected packet number in the receive handler.
vPMDを高スループットに焦点を当てているように、RXバーストサイズに等しいかバースト当たり32以上であると仮定している。
受信ハンドラで予想パケット番号としてnb_pkt <32を使用している場合には 0を返します。
8.6.2. TX Constraint (送信制約)
8.6.2.1. Prerequisite (前提条件)
The only prerequisite is related to tx_rs_thresh.
The tx_rs_thresh value must be greater than or equal to RTE_PMD_IXGBE_TX_MAX_BURST, but less or equal to RTE_IXGBE_TX_MAX_FREE_BUF_SZ.
Consequently, by default the tx_rs_thresh value is in the range 32 to 64.
唯一の前提条件は、tx_rs_threshに関連している。
tx_rs_thresh値は、以上RTE_PMD_IXGBE_TX_MAX_BURSTに等しいが、RTE_IXGBE_TX_MAX_FREE_BUF_SZに以下でなければなりません。
このため、デフォルトではtx_rs_thresh値が範囲内に32から64である。

8.6.2.2. Feature not Supported by RX Vector PMD
TX vPMD only works when txq_flags is set to IXGBE_SIMPLE_FLAGS.
This means that it does not support TX multi-segment, VLAN offload and TX csum offload.
The following MACROs are used for these three features:
- ETH_TXQ_FLAGS_NOMULTSEGS
- ETH_TXQ_FLAGS_NOVLANOFFL
- ETH_TXQ_FLAGS_NOXSUMSCTP
- ETH_TXQ_FLAGS_NOXSUMUDP
- ETH_TXQ_FLAGS_NOXSUMTCP
TX vPMDは、txq_flagsがIXGBE_SIMPLE_FLAGSに設定されている場合にのみ機能します。
これは、TXマルチセグメント、VLANオフロードとTX CS​​UMオフロードをサポートしていないことを意味します。
次のマクロは、これらの三つの特徴のために使用されます。
  • ETH_TXQ_FLAGS_NOMULTSEGS
  • ETH_TXQ_FLAGS_NOVLANOFFL
  • ETH_TXQ_FLAGS_NOXSUMSCTP
  • ETH_TXQ_FLAGS_NOXSUMUDP
  • ETH_TXQ_FLAGS_NOXSUMTCP

8.6.3. Sample Application Notes





















22. IP Fragmentation and Reassembly Library

The IP Fragmentation and Reassembly Library implements IPv4 and IPv6 packet fragmentation and reassembly.
IPフラグメント・リアセンブルライブラリは、IPv4とIPv6のフラグメンテーションとリアセンブリを実装しています。

22.1. Packet fragmentation

Packet fragmentation routines devide input packet into number of fragments.
Both rte_ipv4_fragment_packet() and rte_ipv6_fragment_packet() functions assume that input mbuf data points to the start of the IP header of the packet
(i.e. L2 header is already stripped out).
To avoid copying for the actual packet's data zero-copy technique is used (rte_pktmbuf_attach).
For each fragment two new mbufs are created:
  • Direct mbuf – mbuf that will contain L3 header of the new fragment.
  • Indirect mbuf – mbuf that is attached to the mbuf with the original packet. It's data field points to the start of the original packets data plus fragment offset.
パケットフラグメンテーションルーチンは、フラグメントの数に入力パケットを分割する。
rte_ipv4_fragment_packet()関数もrte_ipv6_fragment_packet()関数も、入力のmbufデータが、パケットのIPヘッダ先頭を指していることを、仮定しています。
(すなわち、L2ヘッダ=ethernet headerは既にストリップされていることを前提としています)
実際のパケットのデータのコピーを回避するために、ゼロコピー技術が使用されている(rte_pktmbuf_attach()関数)。
各フラグメントのために、2つの新しいmbufが作成される。
  • ダイレクトmbuf: 新しいフラグメントのL3ヘッダを構成するmbuf
  • インダイレクトmbuf: オリジナルパケットにattachされたmbuf. このデータフィールドはオリジナルパケットデータの先頭にフラグメントオフセットを加算した位置を指している。
Then L3 header is copied from the original mbuf into the 'direct' mbuf and updated to reflect new fragmented status.
Note that for IPv4, header checksum is not recalculated and is set to zero.

Finally 'direct' and 'indirect' mbufs for each fragnemt are linked together via mbuf's next filed to compose a packet for the new fragment.

The caller has an ability to explicitly specify which mempools should be used to allocate 'direct' and 'indirect' mbufs from.
L3ヘッダはオリジナルmbufから、ダイレクトmbufにコピーされ、新しいフラグメントステータスに更新される。
注意:IPv4については、ヘッダチェックサムは再計算されず、ゼロにセットされます。
呼び出し側には、明示的にどのmempoolsからダイレクトとインダイレクトのmbufを割り当てるのに使用するかを指定する機能があります。
Note that configuration macro RTE_MBUF_SCATTER_GATHER has to be enabled to make fragmentation library build and work correctly.
For more information about direct and indirect mbufs, refer to the DPDK Programmers guide "7.7 Direct and Indirect Buffers".
注意:コンフィギュレーションマクロ、"RTE_MBUF_SCATTER_GATHER"は、フラグメンテーションライブラリをビルド、
正しく機能させるために有効にする必要があります。

22.2. Packet reassembly

22.2.1. IP Fragment Table
Fragment table maintains information about already received fragments of the packet.

Each IP packet is uniquely identified by triple , , .
フラグメントテーブルは、分割されたあるパケットの既に受信した情報を維持する。
各IPパケットは、SRC-IP,DST-IP,IDの3つによって一意に識別されます。
Note that all update/lookup operations on Fragmen Table are not thread safe.
So if different execution contexts (threads/processes) will access the same table simultaneously, then some exernal syncing mechanism have to be provided.

Each table entry can hold information about packets consisting of up to RTE_LIBRTE_IP_FRAG_MAX (by default: 4) fragments.
フラグメントテーブル上の全ての更新/検索操作は、スレッドセーフではないことに注意してください。
異なる実行コンテキスト(スレッド/プロセス)で、同時に同じテーブルをアクセスする場合は、外部の同期メカニズムを提供する必要があります。
各テーブルエントリは、上限RTE_LIBRTE_IP_FRAG_MAX(標準で4)分割で構成されるパケットの情報を保持することができます。
Code example, that demonstrates creation of a new Fragment table:
新しいフラグメントテーブルを作成するデモのコード例::

>frag_cycles = (rte_get_tsc_hz() + MS_PER_S - 1) / MS_PER_S * max_flow_ttl;
>bucket_num = max_flow_num + max_flow_num / 4;
>frag_tbl = rte_ip_frag_table_create(max_flow_num, bucket_entries, max_flow_num, frag_cycles, socket_id);
Internally Fragment table is a simple hash table.
The basic idea is to use two hash functions and * associativity.
This provides 2 * possible locations in the hash table for each key.
When the collision occurs and all 2 * are occupied, instead of reinserting existing keys into alternative locations, ip_frag_tbl_add() just returns a faiure.
フラグメントテーブルの内部は、単純なハッシュテーブルです。
基本のアイデアは、2つのハッシュ関数と、 *の結合性です。
これは、それぞれのキーのハッシュテーブル内の"2 * "可能な位置を提供する。
衝突が発生し、すべてのbucket_entriesが占有されている場合には、代わりに別の場所に既存のキーを再挿入するため、ip_frag_tbl_add()は単にfaiureを返します。
Also, entries that resides in the table longer then are considered as invalid, and could be removed/replaced by the new ones.

Note that reassembly demands a lot of mbuf's to be allocated.
At any given time up to (2 * bucket_entries * RTE_LIBRTE_IP_FRAG_MAX * ) can be stored inside Fragment Table waiting for remaining fragments.
また、より長い、テーブル内に存在するエントリは、無効とみなされ、新しいものに置き換え/除去することができる。
リアセンブリは、mbufの多くが割り当てられるように要求することに注意してください。

22.2.