コードをクリーンにする:Linuxカーネルで修正できるもの

確かに多くの人がLinuxカーネルの何かをより良いものに変えようとしますが、どこから始めればいいのかわかりません。 誰もが修正できるいくつかの問題を説明し、問題を見つけてメーリングリストに修正を公開する方法を示す例を使用します。 物語の過程で、読者はいくつかの補助ユーティリティに精通します。

年々、同じ些細な問題がドライバーからドライバーへとさまよう。これは、Linuxカーネルに存在するいくつかの標準的な慣行、ユーティリティ、または拡張機能の無知に関連することが多い。

これらの種類の問題の短いリストを次に示します。



誤字と誤記



ドキュメントおよびコメント内のタイプミスやタイプミスは珍しくありません。 ある人、つまりLucas De Marchiは、このようなタイプミスをキャッチするための特別なコードスペルユーティリティを開発しました。

次の例は、自分自身に関するすべての情報を提供します。

カーネルのクローンを作成します。
mkdir ~/devel cd ~/devel git clone git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git cd ~/devel/linux-next 

最新の、つまりlinux-nextツリーで作業することに注意してください。

codespellテスト実行をcodespellます。
 $ codespell.py drivers/staging/unisys drivers/staging/unisys/include/guestlinuxdebug.h:138: doesnt ==> doesn't 

今すぐ修正:
 $ codespell.py -w -i 3 drivers/staging/unisys * doesnt show, so we doesnt ==> doesn't (Y/n) y FIXED: drivers/staging/unisys/include/guestlinuxdebug.h 

何が起こったのか見てみましょう:
 --- a/drivers/staging/unisys/include/guestlinuxdebug.h +++ b/drivers/staging/unisys/include/guestlinuxdebug.h @@ -135,7 +135,7 @@ enum event_pc { /* POSTCODE event identifier tuples */ #define POSTCODE_SEVERITY_ERR DIAG_SEVERITY_ERR #define POSTCODE_SEVERITY_WARNING DIAG_SEVERITY_WARNING #define POSTCODE_SEVERITY_INFO DIAG_SEVERITY_PRINT /* TODO-> Info currently - * doesnt show, so we + * doesn't show, so we * set info=warning */ /* example call of POSTCODE_LINUX_2(VISOR_CHIPSET_PC, POSTCODE_SEVERITY_ERR); * Please also note that the resulting postcode is in hex, so if you are 



出力のカスタム実装



コードの読みやすさとマイクロ最適化の改善ほど問題ではありません。多くの場合、関数が呼び出されたときにスタックで使用されるメモリ量が大幅に減少し、vsnprintf()で渡される指定子の数が減少します。

前に、カーネルの%p指定子特別な拡張について説明しましたが、今度は取得した知識を適用する番です。


簡単にするために、テンプレートを取る
%02x [-:]%02x [-:]%02x これにより、スタックを介した数バイトの転送を見つけることができます。これは、拡張子%* ph [CDN]に置き換えることができます。

コードを見てみましょう:
 $ git grep -n -i -e '%02x[-: ]%02x[-: ]%02x' drivers/staging/unisys drivers/staging/unisys/virtpci/virtpci.c:1313: "[%d:%d] VNic:%02x:%02x:%02x:%02x:%02x:%02x num_rcv_bufs:%d mtu:%d", 


次に行うことは、以下の例で説明します。 それまでの間、次の問題領域に進みます。


アルゴリズムのカスタム実装



ここで、たとえば、drivers / staging / fbtft / fbtft-bus.c、99〜100行目を取り上げます。
 for (i = 0; i < pad; i++) *buf++ = 0x000; 

padはu16として定義され、0〜3の範囲、つまり0〜6バイトにすることができます。 私たちが知っているように、memset()は、特に小さなサイズで非常に最適化された関数です。 申し込む理由

または、同じドライバーの別の例、つまり、drivers / staging / fbtft / fbtft-core.c、行1091-1096:
  /* make debug message */ msg[0] = '\0'; for (j = 0; j < i; j++) { snprintf(str, 128, " %02X", buf[j]); strcat(msg, str); } 

カーネルにbin2hex()が存在することを人々は知りませんでした。strcat()が完全に不要であることは言うまでもありません-snprintf()は終了 '\ 0'を追加します。

自分で修正してみてください。

誰もあなたがこれを単純化する方法をすでに見ていますか?
実際、16進形式でのダンプ出力にはバッファーが必要なので、ループ、msg変数を削除し、渡された長さiの%* ph指定子で置き換えるか、print_hex_bytes()を呼び出します。

コードに同様の注意をさらに払うと、会社はそれを最適化することができます:行1192-1202。



既存の定数とデータ型の定義



unisysドライバーに戻り、次のコマンドを実行します。
 $ git grep -n MAX_MACADDR_LEN drivers/staging/unisys/ drivers/staging/unisys/common-spar/include/channels/iochannel.h:190:#ifndef MAX_MACADDR_LEN drivers/staging/unisys/common-spar/include/channels/iochannel.h:191:#define MAX_MACADDR_LEN 6 /* number of bytes… drivers/staging/unisys/common-spar/include/channels/iochannel.h:192:#endif …  … 


ただし、カーネルでMACアドレス長の定数がETH_ALENとして長い間定義されていることに注意してください。 メンテナは、あなたの定義を標準的な核のものに置き換えるパッチを喜んで受け入れるでしょう。


修正例



スムーズに練習を進めます。 上記で、いくつかのバイトの出力がスタックを介した各バイトの転送を使用する場所を見つけました。

コードを調べると、次のことがわかります。
 str_pos += scnprintf(vbuf + str_pos, len - str_pos, "[%d:%d] VNic:%02x:%02x:%02x:%02x:%02x:%02x num_rcv_bufs:%d mtu:%d", tmpvpcidev->bus_no, tmpvpcidev->device_no, tmpvpcidev->net.mac_addr[0], tmpvpcidev->net.mac_addr[1], tmpvpcidev->net.mac_addr[2], tmpvpcidev->net.mac_addr[3], tmpvpcidev->net.mac_addr[4], tmpvpcidev->net.mac_addr[5], tmpvpcidev->net.num_rcv_bufs, tmpvpcidev->net.mtu); 

そして、誰かがMACアドレスを表示することがわかりました! さて、特別な修飾子拡張- %pMを簡単に使用できます。

置き換えて、結果を見てみましょう。
 --- a/drivers/staging/unisys/virtpci/virtpci.c +++ b/drivers/staging/unisys/virtpci/virtpci.c @@ -1310,15 +1310,10 @@ static ssize_t info_debugfs_read(struct file *file, char __user *buf, tmpvpcidev->scsi.max.cmd_per_lun); } else { str_pos += scnprintf(vbuf + str_pos, len - str_pos, - "[%d:%d] VNic:%02x:%02x:%02x:%02x:%02x:%02x num_rcv_bufs:%d mtu:%d", + "[%d:%d] VNic:%pM num_rcv_bufs:%d mtu:%d", tmpvpcidev->bus_no, tmpvpcidev->device_no, - tmpvpcidev->net.mac_addr[0], - tmpvpcidev->net.mac_addr[1], - tmpvpcidev->net.mac_addr[2], - tmpvpcidev->net.mac_addr[3], - tmpvpcidev->net.mac_addr[4], - tmpvpcidev->net.mac_addr[5], + tmpvpcidev->net.mac_addr, tmpvpcidev->net.num_rcv_bufs, tmpvpcidev->net.mtu); } 

それはいいようです-スタック上の5行とより少ない変数。 結果をコンパイルする価値はまだあります。 これがどのように行われるかについては詳しく説明しませんが、オプションを使用して構成でドライバーを有効にする必要があることを示すだけです。
CONFIG_STAGING = y
CONFIG_UNISYSPAR = y
CONFIG_UNISYS_VIRTPCI = m

git commit -a -sを使用してツリーに変更を保存し、パッチとしてフォーマットします。
 $ git format-patch HEAD~1 0001-staging-unisys-print-MAC-address-via-pM.patch 

次に、すばらしいget_maintainter.plスクリプトを使用して、個人に通知する必要がある人を見つけます。
 $ scripts/get_maintainer.pl --git-min-percent=67 --nor --norolestats 00* Benjamin Romer <benjamin.romer@unisys.com> David Kershner <david.kershner@unisys.com> Greg Kroah-Hartman <gregkh@linuxfoundation.org> sparmaintainer@unisys.com devel@driverdev.osuosl.org linux-kernel@vger.kernel.org 

次のアドレスにパッチを送信します。
 $ git send-email --cc-cmd 'scripts/get_maintainer.pl --git-min-percent=67 --nor --nol --norolestats' 00* 0001-staging-unisys-print-MAC-address-via-pM.patch Who should the emails be sent to (if any)? devel@driverdev.osuosl.org, sparmaintainer@unisys.com Message-ID to be used as In-Reply-To for the first email (if any)? … Send this email? ([y]es|[n]o|[q]uit|[a]ll): y 

ここに小さな手紙があります。

更新:すでにカーネルに含まれています: 9a836c0a6310e6e9

Source: https://habr.com/ru/post/J253123/


All Articles