目次: 車
引っ越してから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月15日
25年10月18日
22年5月5日
25年10月19日
23年4月11日
06年4月22日
25年10月17日
25年10月6日
25年10月13日
20年10月23日
25年10月12日
20年8月29日
19年1月13日
18年10月13日
18年9月3日
18年8月20日
18年7月23日
18年7月22日
18年10月14日
18年11月10日
wiki
Linux JM
Java API
2002年
2003年
2004年
2005年
2006年
2007年
2008年
2009年
2010年
2011年
2012年
2013年
2014年
2015年
2016年
2017年
2018年
2019年
2020年
2021年
2022年
2023年
2024年
2025年
過去日記について
アクセス統計
サーバ一覧
サイトの情報合計:
本日: