FreeBSDでChromium (Chrome)をブラウザとしてお使いのかたに朗報です。長いこと悩まされ続けてきた「Chromiumのタブがしばしばハングアップする」という問題がついに解決しました!
Chromiumユーザであればおなじみの不具合だと思いますが、いちおうおさらいしておきましょう。発生していたおもな症状は以下の二つです。
- URLを開くときにローディング状態のまま進まない(既存タブ、新規タブいずれの場合でも発生)
- マウスやキーボードでの操作に反応しなくなる(例: スクロールの途中で反応しなくなる)
さらにひどくなるとブラウザ全体がフリーズしてしまい、操作にまったく反応しなくなることもありました。こうなると、タブのクローズや切り替えすらもできなくなり、コマンドラインから強制的にkill
せざるを得なくなってしまいます。
先日の記事で、この問題を緩和する方法をいくつか紹介しましたが、根本的な解決にはいたらず、症状が発生する頻度をやや低くする程度の効果しかありませんでした。
このような状況だったわけですが、8/4付けでカーネルのソケットバッファまわりに以下の修正が入りました。(8/17付けでstable/11ブランチにもマージされています。)
Chromiumのタブがハングする問題に関するバグレポートスレッド(Bug 212812)で、上記の修正によってタブハング問題が解決されたとのコメント、あるいはこれに同意するコメントが見られます(Comment 96あたりから)。(ちなみにこのバグ、報告されたのが2016年9月なので、もうまる二年になる息の長い問題だったのですね。)
一刻も早くこの恩恵にあずかりたいと思い、手もとのマシン(11.2-RELEASE-p2が動作)に対して上記の変更を適用してみました。すると、確かにはっきりとした効果があります。(変更を適用してから数日間Chromiumを使用していますが、まだタブがハングする現象にあっていません。)
ただし、上記の変更をそのまま11.2-RELEASE-p2に適用すると、一部パッチがうまく当たらない部分がありました。そこで、11.2-RELEASE-p2向けのパッチファイルを作成しました(本記事の末尾参照)。本ファイルを適当な場所に保存して以下の手順を実行すると恩恵にあずかることができます。
svnlite checkout https://svn.freebsd.org/base/releng/11.2 /usr/src # 手もとにソースがない場合のみ
cd /usr/src
patch -p0 < /path/to/patch/file
make buildkernel
make installkernel
reboot
参考文献
- Revision 337328 - Don’t check rcv sockbuf limits when sending on a unix stream socket, https://svnweb.freebsd.org/base?view=revision&revision=337328
- Revision 337975 - MFC r337328, https://svnweb.freebsd.org/base?view=revision&revision=337975
- Bug 212812 - www/chromium: tabs “hang” 10% of the time, https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=212812
11.2-RELEASE-p2に対するパッチ
注: 手もとのマシンで問題なく動作することを確認していますが、パッチの使用は自己責任でおねがいします。
Index: sys/kern/uipc_sockbuf.c | |
=================================================================== | |
--- sys/kern/uipc_sockbuf.c (revision 337991) | |
+++ sys/kern/uipc_sockbuf.c (working copy) | |
@@ -888,23 +888,14 @@ | |
return (retval); | |
} | |
-int | |
+void | |
sbappendcontrol_locked(struct sockbuf *sb, struct mbuf *m0, | |
struct mbuf *control) | |
{ | |
- struct mbuf *m, *n, *mlast; | |
- int space; | |
+ struct mbuf *m, *mlast; | |
- SOCKBUF_LOCK_ASSERT(sb); | |
- | |
- if (control == NULL) | |
- panic("sbappendcontrol_locked"); | |
- space = m_length(control, &n) + m_length(m0, NULL); | |
- | |
- if (space > sbspace(sb)) | |
- return (0); | |
m_clrprotoflags(m0); | |
- n->m_next = m0; /* concatenate data to control */ | |
+ m_last(control)->m_next = m0; | |
SBLASTRECORDCHK(sb); | |
@@ -918,18 +909,15 @@ | |
SBLASTMBUFCHK(sb); | |
SBLASTRECORDCHK(sb); | |
- return (1); | |
} | |
-int | |
+void | |
sbappendcontrol(struct sockbuf *sb, struct mbuf *m0, struct mbuf *control) | |
{ | |
- int retval; | |
SOCKBUF_LOCK(sb); | |
- retval = sbappendcontrol_locked(sb, m0, control); | |
+ sbappendcontrol_locked(sb, m0, control); | |
SOCKBUF_UNLOCK(sb); | |
- return (retval); | |
} | |
/* | |
Index: sys/kern/uipc_usrreq.c | |
=================================================================== | |
--- sys/kern/uipc_usrreq.c (revision 337991) | |
+++ sys/kern/uipc_usrreq.c (working copy) | |
@@ -981,16 +981,22 @@ | |
unp2->unp_flags &= ~UNP_WANTCRED; | |
control = unp_addsockcred(td, control); | |
} | |
+ | |
/* | |
- * Send to paired receive port, and then reduce send buffer | |
- * hiwater marks to maintain backpressure. Wake up readers. | |
+ * Send to paired receive port and wake up readers. Don't | |
+ * check for space available in the receive buffer if we're | |
+ * attaching ancillary data; Unix domain sockets only check | |
+ * for space in the sending sockbuf, and that check is | |
+ * performed one level up the stack. At that level we cannot | |
+ * precisely account for the amount of buffer space used | |
+ * (e.g., because control messages are not yet internalized). | |
*/ | |
switch (so->so_type) { | |
case SOCK_STREAM: | |
if (control != NULL) { | |
- if (sbappendcontrol_locked(&so2->so_rcv, m, | |
- control)) | |
- control = NULL; | |
+ sbappendcontrol_locked(&so2->so_rcv, m, | |
+ control); | |
+ control = NULL; | |
} else | |
sbappend_locked(&so2->so_rcv, m, flags); | |
break; | |
@@ -999,14 +1005,8 @@ | |
const struct sockaddr *from; | |
from = &sun_noname; | |
- /* | |
- * Don't check for space available in so2->so_rcv. | |
- * Unix domain sockets only check for space in the | |
- * sending sockbuf, and that check is performed one | |
- * level up the stack. | |
- */ | |
if (sbappendaddr_nospacecheck_locked(&so2->so_rcv, | |
- from, m, control)) | |
+ from, m, control)) | |
control = NULL; | |
break; | |
} | |
Index: sys/sys/sockbuf.h | |
=================================================================== | |
--- sys/sys/sockbuf.h (revision 337991) | |
+++ sys/sys/sockbuf.h (working copy) | |
@@ -146,9 +146,9 @@ | |
struct mbuf *m0, struct mbuf *control); | |
int sbappendaddr_nospacecheck_locked(struct sockbuf *sb, | |
const struct sockaddr *asa, struct mbuf *m0, struct mbuf *control); | |
-int sbappendcontrol(struct sockbuf *sb, struct mbuf *m0, | |
+void sbappendcontrol(struct sockbuf *sb, struct mbuf *m0, | |
struct mbuf *control); | |
-int sbappendcontrol_locked(struct sockbuf *sb, struct mbuf *m0, | |
+void sbappendcontrol_locked(struct sockbuf *sb, struct mbuf *m0, | |
struct mbuf *control); | |
void sbappendrecord(struct sockbuf *sb, struct mbuf *m0); | |
void sbappendrecord_locked(struct sockbuf *sb, struct mbuf *m0); |