目次: 車
引っ越してから1年もの間、車検証の住所変更をせずにほったらかしていました(本当は良くない)が、重い腰を上げ、大阪ナンバーにお別れを告げて、品川ナンバーになりました。
手続きに興味があったので、あえてディーラーにお任せせず自分でやってみました。面白かったですが相当面倒くさいです。もう自分でやることはないでしょうね。
今回は「変更登録」という手続きをしました。車検証の住所変更&ナンバープレートの変更です。他県から東京都に引っ越した人が該当します。手続きはざっくりいうと、
どうがんばって圧縮しても、平日を2回消費します。普通は警察、警察、陸運局、の3回消費すると思います。警察も陸運局も平日しか受け付けしていないので、勤め人にはなかなか辛いです。
興味のある人は居なさそうですが、メモ代わりに書いておきます。まずは車庫証明用の書類が必要です。
ここまでの書類は全部の警視庁のサイト(保管場所証明申請手続 - 警視庁)からゲットできます。
ナンバーの封印を付けてもらったら、そのまま車で運輸局からおさらばです。
やってみた感想は「RPGのクエストみたい」です。書類と書類を交換していくと、最後に車検証とナンバープレートが貰えます、みたいな感じですね。車のナンバー(と自動車税)管理の一環が垣間見えて、なかなか面白い社会科見学でした。
個人的にはナンバープレートの封印を破壊するのは、普段やらない体験で「え?自分で壊すの??」と緊張しました(関東運輸局だけセルフで、他地域ではやらないみたいです)。私の車は粗雑に扱っているせいなのか、フロント側のナンバーのネジが錆びて固着しており、あやうくネジをなめそうでした。次はもう外せないかもしれない。
昨今のコロナ騒ぎのせいか、いずれの窓口もガラッガラに空いていて快適でした。混んでいたら地獄だったと思います。
 この記事にコメントする
 この記事にコメントする
目次: GCC
GCCはアーキテクチャごとに設定ファイルがあってgcc/config/(アーキテクチャ名)/ という名前のディレクトリ下に配置されています。
その中にmachine modeを設定するファイルがあります。例えばRISC-Vならgcc/config/riscv/riscv-modes.defですし、AArch64ならgcc/config/aarch64/aarch64-modes.defです。machine modeとは何かはちょっと横に置いて、このファイルの探し方が割と不思議な作りだったので紹介します。
GCCのビルドプロセスは非常にややこしいです。xxxx-modes.defはGCCのコードで直接使われず、前処理で使われます。GCCのビルドディレクトリを (build_dir) と書くとすると、
こんな仕組みになっています。起点となるgenmodes.cから見ます。
// gcc/genmodes.c
static void
create_modes (void)
{
#include "machmode.def"  //★★これに全てのモードが定義されている
// gcc/machmode.def
/* Allow the target to specify additional modes of various kinds.  */
#if HAVE_EXTRA_MODES
# include EXTRA_MODES_FILE  //★★追加のファイルがあればinclude
#endif
// gcc/genmodes.c
#ifdef EXTRA_MODES_FILE
# define HAVE_EXTRA_MODES 1
#else
# define HAVE_EXTRA_MODES 0
# define EXTRA_MODES_FILE ""
#endif
EXTRA_MODES_FILEというマクロにファイル名が定義されていれば、そのファイルもincludeする仕組みになっています。C言語を見慣れている人もかなり面食らう書き方ですが、GCCは「*.defファイルをincludeする」という技を乱発します。メチャクチャすぎる。
EXTRA_MODES_FILEをマクロを定義するのはconfig.gccとconfigureです。
# gcc/config.gcc
extra_modes=
if test -f ${srcdir}/config/${cpu_type}/${cpu_type}-modes.def
then
	extra_modes=${cpu_type}/${cpu_type}-modes.def    # ★★aarch64-modes.defのような名前のファイルが存在すればそのファイルを使う
fi
# gcc/configure
# Collect target-machine-specific information.
. ${srcdir}/config.gcc || exit 1    # ★★config.gccの設定を取り込む
...
# Look for a file containing extra machine modes.
if test -n "$extra_modes" && test -f $srcdir/config/$extra_modes; then
  extra_modes_file='$(srcdir)'/config/${extra_modes}    # ★★extra_modesはconfig.gccで定義したものが導入される
cat >>confdefs.h <<_ACEOF
#define EXTRA_MODES_FILE "config/$extra_modes"    # ★★config/aarch64/aarch64-modes.defのような値になる
_ACEOF
fi
やっとたどり着きました。GCCってちょっとしたことでも、ものすごく複雑にできていて、読むのが辛いというか、読んでも意味不明なことが多いです。うーん、辛い。
 この記事にコメントする
 この記事にコメントする
37歳になりました。おめでとう俺、ありがとう俺。30代も残りわずかです。
何歳になろうが、本人は特に何も変わってませんが、周りから見ると確実にオジサンです。若者へ説教と昔の自慢話だけは絶対しないように気をつけます。
今に始まったことじゃないんですが、いつも何歳だか良くわからなくなります。もちろん (現在の西暦) - (生まれた西暦) で、1月1日〜3月9日は -1すれば良いのはわかってますが、ぱっと答えられません。なぜなのか……。
 この記事にコメントする
 この記事にコメントする
目次: ARM
ASUS TinkerBoardで動かしているlinux-nextを最新版に更新したところ、下記のようなエラーを出力して起動しなくなりました。
[ 0.000000] __clk_core_init: Failed to get phase for clk 'sdmmc_drv' [ 0.000000] rockchip_clk_register_branches: failed to register clock sdmmc_drv: -22 [ 0.000000] rockchip_clk_register_branches: failed to register clock sdmmc_sample: -17 [ 0.000000] rockchip_clk_register_branches: failed to register clock sdio0_drv: -17 [ 0.000000] rockchip_clk_register_branches: failed to register clock sdio0_sample: -17 [ 0.000000] rockchip_clk_register_branches: failed to register clock sdio1_drv: -17 [ 0.000000] rockchip_clk_register_branches: failed to register clock sdio1_sample: -17 [ 0.000000] rockchip_clk_register_branches: failed to register clock emmc_drv: -17 [ 0.000000] rockchip_clk_register_branches: failed to register clock emmc_sample: -17
エラーメッセージで検索しても、特にめぼしい報告やパッチに引っかかりません。うーん。困りましたね。
仕方ないので、更新履歴をgit bisectすると2/13から2/14の間にクロックドライバが変更されたことが原因だとわかりました。
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index dc8bdfbd6a0c..ed1797857bae 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -3457,7 +3457,12 @@ static int __clk_core_init(struct clk_core *core)
 	 * Since a phase is by definition relative to its parent, just
 	 * query the current clock phase, or just assume it's in phase.
 	 */
-	clk_core_get_phase(core);
+	ret = clk_core_get_phase(core);
+	if (ret < 0) {
+		pr_warn("%s: Failed to get phase for clk '%s'\n", __func__,
+			core->name);
+		goto out;
+	}
 
 	/*
 	 * Set clk's duty cycle.
しかしこの変更は真っ当に見えます。今までエラーを無視していたところをエラーチェックするようにしているだけだからです。
どの辺りでエラーが出るか調べるためprintkを適当に入れたりしながら追ってみると、下記の場所で躓いていました。
// drivers/clk/clk.c
static int __clk_core_init(struct clk_core *core)
{
...
	/*
	 * Set clk's phase by clk_core_get_phase() caching the phase.
	 * Since a phase is by definition relative to its parent, just
	 * query the current clock phase, or just assume it's in phase.
	 */
	phase = clk_core_get_phase(core);  //★★変更が影響している場所
	if (phase < 0) {
		ret = phase;
		pr_warn("%s: Failed to get phase for clk '%s'\n", __func__,
			core->name);
		//goto out;
	}
// drivers/clk/clk.c
static int clk_core_get_phase(struct clk_core *core)
{
	int ret;
	lockdep_assert_held(&prepare_lock);
	if (!core->ops->get_phase)
		return 0;
	/* Always try to update cached phase if possible */
	ret = core->ops->get_phase(core->hw);  //★★ここでエラーが発生していた
	if (ret >= 0)
		core->phase = ret;
	return ret;
}
独自のget_phaseを持っているドライバがエラーを返すと、clk_core_get_phase() もエラーを返し、先程のエラーメッセージが表示されるみたいです。しかし、この処理自体におかしな箇所はないように思います。真っ当です。
おそらく実装がおかしいのはRockchipのMMCドライバのクロック周りの方でしょう。エラーメッセージに出ているsdmmc_drvを頼りにコードを追います。
// drivers/clk/rockchip/clk-rk3228.c
PNAME(mux_mmc_src_p)		= { "cpll", "gpll", "xin24m", "usb480m" };
...
	COMPOSITE(SCLK_SDMMC, "sclk_sdmmc", mux_mmc_src_p, 0,
			RK2928_CLKSEL_CON(11), 8, 2, MFLAGS, 0, 8, DFLAGS,
			RK2928_CLKGATE_CON(2), 11, GFLAGS),
	...
	//★★sdmmc_drvはこの部分由来です
	MMC(SCLK_SDMMC_DRV,    "sdmmc_drv",    "sclk_sdmmc", RK3228_SDMMC_CON0, 1),
// drivers/clk/rockchip/clk.h
#define MMC(_id, cname, pname, offset, shift)			\
	{							\
		.id		= _id,				\
		.branch_type	= branch_mmc,			\  //★★この値がカギかな?
		.name		= cname,			\
		.parent_names	= (const char *[]){ pname },	\
		.num_parents	= 1,				\
		.muxdiv_offset	= offset,			\
		.div_shift	= shift,			\
	}
// drivers/clk/rockchip/clk-rk3228.c
static void __init rk3228_clk_init(struct device_node *np)
{
...
	rockchip_clk_register_branches(ctx, rk3228_clk_branches,  //★★ここでbranch_typeを見ている箇所がある
				  ARRAY_SIZE(rk3228_clk_branches));
...
}
//★★注: rk3228_clk_initはRK3228のクロックコア初期化関数
//    デバイスツリー内のcompatible = "rockchip,rk3228-cru" を持つノードに応じて呼ばれる
CLK_OF_DECLARE(rk3228_cru, "rockchip,rk3228-cru", rk3228_clk_init);
// drivers/clk/rockchip/clk.c
void __init rockchip_clk_register_branches(
				      struct rockchip_clk_provider *ctx,
				      struct rockchip_clk_branch *list,
				      unsigned int nr_clk)
{
...
	for (idx = 0; idx < nr_clk; idx++, list++) {
		flags = list->flags;
		/* catch simple muxes */
		switch (list->branch_type) {
		...
		case branch_mmc:  //★★branch_type == branch_mmcだったら
			clk = rockchip_clk_register_mmc(  //★★クロックの登録をしている
				list->name,
				list->parent_names, list->num_parents,
				ctx->reg_base + list->muxdiv_offset,
				list->div_shift
			);
			break;
		...
// drivers/clk/rockchip/clk-mmc-phase.c
struct clk *rockchip_clk_register_mmc(const char *name,
				const char *const *parent_names, u8 num_parents,
				void __iomem *reg, int shift)
{
...
	init.name = name;
	init.flags = 0;
	init.num_parents = num_parents;
	init.parent_names = parent_names;
	init.ops = &rockchip_mmc_clk_ops;  //★★クロックの操作関数を登録している
...
// drivers/clk/rockchip/clk-mmc-phase.c
static const struct clk_ops rockchip_mmc_clk_ops = {
	.recalc_rate	= rockchip_mmc_recalc,
	.get_phase	= rockchip_mmc_get_phase,  //★★get_phaseはこの関数
	.set_phase	= rockchip_mmc_set_phase,
};
// drivers/clk/rockchip/clk-mmc-phase.c
static int rockchip_mmc_get_phase(struct clk_hw *hw)
{
	struct rockchip_mmc_clock *mmc_clock = to_mmc_clock(hw);
	unsigned long rate = clk_hw_get_rate(hw);
	u32 raw_value;
	u16 degrees;
	u32 delay_num = 0;
	/* See the comment for rockchip_mmc_set_phase below */
	if (!rate)
		return -EINVAL;  //★★エラーを返している
...
原因らしき箇所が見つかりました。クロック周波数(rate)が設定されていなくても、エラーを返す必要はなく、phaseの初期値0を返せば良いはずですから、return -EINVALをreturn 0にすれば良さそうです。
変更すると無事linux-nextが起動できるようになりました。良かった良かった。
こんなバグを2週間以上放置するなんてlinuxらしくないなと思って、パッチを送ろうとしましたが……、なんだかとっても嫌な予感がしたので、rochchip_mmc_get_phaseでLKMLを検索してみました。
検索してびっくり、なんと、全く同じ指摘をしているパッチが先週LKMLに送られています(LKMLへのリンク)。しかも既にclk-nextに取り込まれているじゃないですか。ですがlinux-nextへの反映はまだのようです。
明日まで待っていればclk-nextがlinux-nextに取り込まれるので、何もしなくても解決していたんです。パッチ投稿が3/4なので、今回は本当に気づくタイミングが悪かったです。
Author: Jerome Brunet < >
Date:   Tue Mar 3 20:29:56 2020 +0100
    clk: rockchip: fix mmc get phase
    
    If the mmc clock has no rate, it can be assumed to be constant.
    In such case, there is no measurable phase shift. Just return 0
    in this case instead of returning an error.
    
    Fixes: 2760878662a2 ("clk: Bail out when calculating phase fails during clk
    registration")
    Tested-by: Markus Reichl <m.reichl@fivetechno.de>
    Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
diff --git a/drivers/clk/rockchip/clk-mmc-phase.c b/drivers/clk/rockchip/clk-mmc-phase.c
index 4abe7ff31f53..975454a3dd72 100644
--- a/drivers/clk/rockchip/clk-mmc-phase.c
+++ b/drivers/clk/rockchip/clk-mmc-phase.c
@@ -51,9 +51,9 @@ static int rockchip_mmc_get_phase(struct clk_hw *hw)
 	u16 degrees;
 	u32 delay_num = 0;
 
-	/* See the comment for rockchip_mmc_set_phase below */
+	/* Constant signal, no measurable phase shift */
 	if (!rate)
-		return -EINVAL;
+		return 0;
 
 	raw_value = readl(mmc_clock->reg) >> (mmc_clock->shift);
前にも、原因を突き止めたら、解決済みだったというパターン(2018年12月4日の日記、2019年9月18日の日記)がありました。
またこの「鶏と卵のパターン」にやられました。ついてない。
コードに特攻する前に、ちゃんと探せば?って思われるかもしれませんが、困ったことにエラーメッセージで検索してもパッチに辿り着けないんです。調べに調べて原因と対策がわかった後に、はぁ?もうパッチあるじゃん!?と気づくから、徒労感が激しい……。
 この記事にコメントする
 この記事にコメントする
目次: GCC
GCCのインラインアセンブラでは __asm__("ins %0" : "=r"(aaa) : : ); のように、"=r" という不思議な文字列が出てきます。
これはconstraintsと呼ばれ(GCCのマニュアルへのリンク)、引数がレジスタ(r)なのかメモリアドレス(m)なのか、書き換えられるのか(=)などを説明しています。
どんな文字でも書けるわけではなく、変な文字(例えば 'v')を指定すると「impossible constraint in 'asm'」と怒られます。これは一体どこでチェックしているのでしょう?また、どうやって足せばよいでしょうか?
このエラーを出すのは、パスでいうと234r.vregsです。
ちなみにbuild_gccというディレクトリ名はGCCのビルドディレクトリのことです。GCCは自動生成コードをかなりの量出力するので、そちらも合わせて見る必要があります。
// gcc/function.c
static void
instantiate_virtual_regs_in_insn (rtx_insn *insn)
{
...
  if (asm_noperands (PATTERN (insn)) >= 0)
    {
      if (!check_asm_operands (PATTERN (insn)))  //★★このエラーチェックに引っかかっている
	{
	  error_for_asm (insn, "impossible constraint in %<asm%>");  //★★このエラーメッセージが出ている
// gcc/recog.c
int
check_asm_operands (rtx x)
{
...
  for (i = 0; i < noperands; i++)
    {
      const char *c = constraints[i];
      if (c[0] == '%')
	c++;
      if (! asm_operand_ok (operands[i], c, constraints))  //★★このエラーチェックに引っかかっている
	return 0;
    }
このチェックは何をしているのかというと、lookup_constraint() で文字(例えば 'v')をテーブルから探し、constraintの種類に変換します。未知の文字の場合はCONSTRAINT_UNKNOWNになります。
次にget_constraint_type() でどのカテゴリに属するか見ます。この関数はおかしくて、なぜかCONSTRAINT__UNKNOWNをCT_REGISTERと判定します。レジスタじゃないですよね?意味不明です。
// gcc/recog.c
int
asm_operand_ok (rtx op, const char *constraint, const char **constraints)
{
  int result = 0;
...
  while (*constraint)
    {
      enum constraint_num cn;
      char c = *constraint;
      int len;
      switch (c)
	{
...
	default:
	  cn = lookup_constraint (constraint);  //★★文字からconstraint_numに変換
						//    未知の文字の場合CONSTRAINT__UNKNOWNになる
	  switch (get_constraint_type (cn))
	    {
	    case CT_REGISTER:  //★★なぜかここに行く
	      if (!result
		  && reg_class_for_constraint (cn) != NO_REGS  //★★レジスタのconstraintがNO_REGSになるとNG
		  && GET_MODE (op) != BLKmode
		  && register_operand (op, VOIDmode))
		result = 1;
	      break;
// build_gcc/gcc/tm-preds.h
static inline enum constraint_num
lookup_constraint (const char *p)
{
  unsigned int index = lookup_constraint_array[(unsigned char) *p];  //★★この配列がカギ
  return (index == UCHAR_MAX
          ? lookup_constraint_1 (p)
          : (enum constraint_num) index);
}
enum constraint_num
{
  CONSTRAINT__UNKNOWN = 0,
  CONSTRAINT_r,
  CONSTRAINT_f,
...
static inline enum constraint_type
get_constraint_type (enum constraint_num c)
{
  if (c >= CONSTRAINT_p)
    {
      if (c >= CONSTRAINT_G)
        return CT_FIXED_FORM;
      return CT_ADDRESS;
    }
  if (c >= CONSTRAINT_m)
    return CT_MEMORY;
  if (c >= CONSTRAINT_I)
    return CT_CONST_INT;
  return CT_REGISTER;  //★★cがCONSTRAINT__UNKNOWNつまり0の場合、どれにも当てはまらずCT_REGISTERと判断される
}
// build_gcc/insn-preds.c
const unsigned char lookup_constraint_array[] = {
  CONSTRAINT__UNKNOWN,
  CONSTRAINT__UNKNOWN,
...
  MIN ((int) CONSTRAINT_r, (int) UCHAR_MAX),
  MIN ((int) CONSTRAINT_s, (int) UCHAR_MAX),
  CONSTRAINT__UNKNOWN,
  CONSTRAINT__UNKNOWN,
  CONSTRAINT__UNKNOWN,  //★★'v' は未定義、未知の文字の場合は全てCONSTRAINT__UNKNOWNになる
  CONSTRAINT__UNKNOWN,
...
// build_gcc/gcc/tm-preds.h
static inline enum reg_class
reg_class_for_constraint (enum constraint_num c)
{
  if (insn_extra_register_constraint (c))
    return reg_class_for_constraint_1 (c);  //★★ここで見つからないとNO_REGSが返されてNG
  return NO_REGS;
}
// build_gcc/gcc/insn-preds.c
enum reg_class
reg_class_for_constraint_1 (enum constraint_num c)
{
  switch (c)
    {
    case CONSTRAINT_r: return GENERAL_REGS;
    case CONSTRAINT_f: return TARGET_HARD_FLOAT ? FP_REGS : NO_REGS;
    case CONSTRAINT_j: return SIBCALL_REGS;
    case CONSTRAINT_l: return JALR_REGS;
    default: break;  //★★CONSTRAINT__UNKNOWNはどのcaseにも当てはまらないのでNO_REGSが返されてNG
    }
  return NO_REGS;
}
明らかにレジスタとは思えないCONSTRAINT__UNKNOWNが返ってきますが、なぜかCT_REGISTERだと思って処理し始め、最終的にエラーとして弾きます。結果オーライですがこれで良いんでしょうか。GCCのコードは訳がわかりません……。
ではvを正当なconstraintの一員にするにはどうしたら良いでしょうか?
第一歩としてはGCCのconfigディレクトリの下にある *.mdファイル(MarkdownではなくMachine Descriptorです)を編集します。
;; config/riscv/riscv.md
(include "predicates.md")
(include "constraints.md")
;; config/riscv/constraints.md
(define_register_constraint "v" "TARGET_VECTOR ? VP_REGS : NO_REGS"
  "A vector register (if available).")
これを足すと(他にも色々やらないといけないんですけど)、先程のreg_class_for_constraint_1() に変化が生じます。
// build_gcc/gcc/insn-preds.c
enum reg_class
reg_class_for_constraint_1 (enum constraint_num c)
{
  switch (c)
    {
    case CONSTRAINT_r: return GENERAL_REGS;
    case CONSTRAINT_f: return TARGET_HARD_FLOAT ? FP_REGS : NO_REGS;
    case CONSTRAINT_j: return SIBCALL_REGS;
    case CONSTRAINT_v: return TARGET_VECTOR ? VP_REGS : NO_REGS;  //★★これが足されて通過するようになる
    case CONSTRAINT_l: return JALR_REGS;
    default: break;
    }
  return NO_REGS;
}
GCCはこの手のピタゴラスイッチの塊で、何を変えると望みの機能が実装できるか、全くわかりません。こんなもの良くメンテナンスできるなあ、と思います。
 この記事にコメントする
 この記事にコメントする
| < | 2020 | > | ||||
| << | < | 03 | > | >> | ||
| 日 | 月 | 火 | 水 | 木 | 金 | 土 | 
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 
| 8 | 9 | 10 | 11 | 12 | 13 | 14 | 
| 15 | 16 | 17 | 18 | 19 | 20 | 21 | 
| 22 | 23 | 24 | 25 | 26 | 27 | 28 | 
| 29 | 30 | 31 | - | - | - | - | 
 25年10月27日
 25年10月27日
 23年4月10日
 23年4月10日
 25年10月15日
 25年10月15日
 25年10月18日
 25年10月18日
 22年5月5日
 22年5月5日
 25年10月19日
 25年10月19日
 23年4月11日
 23年4月11日
 06年4月22日
 06年4月22日
 25年10月17日
 25年10月17日
 25年10月6日
 25年10月6日
 25年10月13日
 25年10月13日
 20年10月23日
 20年10月23日
 25年10月12日
 25年10月12日
 20年8月29日
 20年8月29日
 19年1月13日
 19年1月13日
 18年10月13日
 18年10月13日
 18年9月3日
 18年9月3日
 18年8月20日
 18年8月20日
 18年7月23日
 18年7月23日
 18年7月22日
 18年7月22日
 wiki
 wiki Linux JM
 Linux JM Java API
 Java API 2002年
 2002年 2003年
 2003年 2004年
 2004年 2005年
 2005年 2006年
 2006年 2007年
 2007年 2008年
 2008年 2009年
 2009年 2010年
 2010年 2011年
 2011年 2012年
 2012年 2013年
 2013年 2014年
 2014年 2015年
 2015年 2016年
 2016年 2017年
 2017年 2018年
 2018年 2019年
 2019年 2020年
 2020年 2021年
 2021年 2022年
 2022年 2023年
 2023年 2024年
 2024年 2025年
 2025年 過去日記について
 過去日記について アクセス統計
 アクセス統計 サーバ一覧
 サーバ一覧 サイトの情報
 サイトの情報合計: 
本日: