09 June 2019

Ubuntu 16.04でIntel GMAのカーネル・モジュールのビルド(dkms install)に失敗する件

Ubuntu 16.04のカーネル アップデート時に、妙なエラーを吐き出しているのを発見した。

apt update; apt upgradeのコンソール出力
〜 略 〜
linux-headers-generic (4.4.0.150.158) を設定しています ...
linux-generic (4.4.0.150.158) を設定しています ...
linux-libc-dev:amd64 (4.4.0-150.176) を設定しています ...
linux-image-4.4.0-150-generic (4.4.0-150.176) のトリガを処理しています ...
/etc/kernel/postinst.d/dkms:
ERROR: Cannot create report: [Errno 17] File exists: '/var/crash/i915-4.6.3-4.4.0-dkms.0.crash'
Error! Bad return status for module build on kernel: 4.4.0-150-generic (x86_64)
Consult /var/lib/dkms/i915-4.6.3-4.4.0/1/build/make.log for more information.
/etc/kernel/postinst.d/initramfs-tools:
〜 略 〜

Intel GMA用のグラフィックス カーネル・モジュールが、dkmsで自動ビルドされるときにエラー停止しているようだ。

ソースコードの修正 (get_user_pages()

apt upgradeで表示されていたログ・ファイルを見てみると

/var/lib/dkms/i915-4.6.3-4.4.0/1/build/make.log
〜 略 〜
/var/lib/dkms/i915-4.6.3-4.4.0/1/build/drivers/gpu/drm/i915/i915_gem_userptr.c:513:7: warning: passing argument 7 of ‘get_user_pages’ from incompatible pointer type [-Wincompatible-pointer-types]
       pvec + pinned, NULL);
       ^
In file included from include/linux/scatterlist.h:7:0,
                 from include/linux/dma-mapping.h:10,
                 from include/drm/drmP.h:37,
                 from /var/lib/dkms/i915-4.6.3-4.4.0/1/build/drivers/gpu/drm/i915/i915_gem_userptr.c:25:
include/linux/mm.h:1222:6: note: expected ‘struct vm_area_struct **’ but argument is of type ‘struct page **’
 long get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
      ^
/var/lib/dkms/i915-4.6.3-4.4.0/1/build/drivers/gpu/drm/i915/i915_gem_userptr.c:508:11: error: too many arguments to function ‘get_user_pages’
     ret = get_user_pages
           ^
In file included from include/linux/scatterlist.h:7:0,
                 from include/linux/dma-mapping.h:10,
                 from include/drm/drmP.h:37,
                 from /var/lib/dkms/i915-4.6.3-4.4.0/1/build/drivers/gpu/drm/i915/i915_gem_userptr.c:25:
include/linux/mm.h:1222:6: note: declared here
 long get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
      ^
失敗した子プロセス 0xf051b0 PID 13416  を回収します
〜 略 〜

get_user_pages() 関数の引数が、宣言より多すぎるとコンパイラがエラーを吐き出している。

現在(kernel 4.4.0-150)のmm.hでの宣言は

/usr/src/linux-headers-4.4.0-150/include/linux/mm.h
long get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
            unsigned long start, unsigned long nr_pages,
            unsigned int gup_flags, struct page **pages,
            struct vm_area_struct **vmas);

kernel 4.4.0の時のmm.hでの宣言は

https://elixir.bootlin.com/linux/v4.4/source/include/linux/mm.h
long get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
            unsigned long start, unsigned long nr_pages,
            int write, int force, struct page **pages,
            struct vm_area_struct **vmas);

問題を起こしているIntel GMAドライバのソースコードは

/usr/src/i915-4.6.3-4.4.0-1/drivers/gpu/drm/i915/i915_gem_userptr.c
static void
__i915_gem_userptr_get_pages_worker(struct work_struct *_work)
{
    struct get_pages_work *work = container_of(_work, typeof(*work), work);
    struct drm_i915_gem_object *obj = work->obj;
    struct drm_device *dev = obj->base.dev;
    const int npages = obj->base.size >> PAGE_SHIFT;
    struct page **pvec;
    int pinned, ret;
 
    ret = -ENOMEM;
    pinned = 0;
 
    pvec = kmalloc(npages*sizeof(struct page *),
               GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY);
    if (pvec == NULL)
        pvec = drm_malloc_ab(npages, sizeof(struct page *));
    if (pvec != NULL) {
        struct mm_struct *mm = obj->userptr.mm->mm;
 
        ret = -EFAULT;
        if (atomic_inc_not_zero(&mm->mm_users)) {
            down_read(&mm->mmap_sem);
            while (pinned < npages) {
                ret = get_user_pages
                    (work->task, mm,
                     obj->userptr.ptr + pinned * PAGE_SIZE,
                     npages - pinned,
                     !obj->userptr.read_only, 0,
                     pvec + pinned, NULL);
                if (ret < 0)
                    break;
 
                pinned += ret;
            }
            up_read(&mm->mmap_sem);
            mmput(mm);
        }
    }

Ubuntu 17.04用のIntel GMAドライバのソースコードを参考に、get_user_pagesを修正し引数を揃える。

引数を変更した新旧ソースコードの差分は次のようなものになる

$ diff -up /home/user/Desktop/修正前/i915_gem_userptr.c /usr/src/i915-4.6.3-4.4.0-1/drivers/gpu/drm/i915/i915_gem_userptr.c
--- /home/user/Desktop/修正前/i915_gem_userptr.c    2016-08-11 07:30:05.000000000 +0900
+++ /usr/src/i915-4.6.3-4.4.0-1/drivers/gpu/drm/i915/i915_gem_userptr.c    2019-06-09 18:05:47.117828575 +0900
@@ -500,6 +500,9 @@ __i915_gem_userptr_get_pages_worker(stru
         pvec = drm_malloc_ab(npages, sizeof(struct page *));
     if (pvec != NULL) {
         struct mm_struct *mm = obj->userptr.mm->mm;
+        unsigned int flags = 0;
+        if (!obj->userptr.read_only)
+            flags |= FOLL_WRITE;
 
         ret = -EFAULT;
         if (atomic_inc_not_zero(&mm->mm_users)) {
@@ -509,7 +512,7 @@ __i915_gem_userptr_get_pages_worker(stru
                     (work->task, mm,
                      obj->userptr.ptr + pinned * PAGE_SIZE,
                      npages - pinned,
-                     !obj->userptr.read_only, 0,
+                     flags,
                      pvec + pinned, NULL);
                 if (ret < 0)
                     break;

ソースコードの修正 (pin_eld_notify()

get_user_pagesの修正を終え、dkmsでビルドする

$ dkms build i915/4.6.3-4.4.0-1

さらにエラーが出力されてビルド失敗。エラー内容は

/var/lib/dkms/i915-4.6.3-4.4.0/1/build/make.log
〜 略 〜
‘intel_audio_codec_enable’:
/var/lib/dkms/i915-4.6.3-4.4.0/1/build/drivers/gpu/drm/i915/intel_audio.c:532:3: error: too few arguments to function ‘acomp->audio_ops->pin_eld_notify’
   acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr, (int) port);
   ^
/var/lib/dkms/i915-4.6.3-4.4.0/1/build/drivers/gpu/drm/i915/intel_audio.c: In function ‘intel_audio_codec_disable’:
/var/lib/dkms/i915-4.6.3-4.4.0/1/build/drivers/gpu/drm/i915/intel_audio.c:560:3: error: too few arguments to function ‘acomp->audio_ops->pin_eld_notify’
   acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr, (int) port);
   ^
/var/lib/dkms/i915-4.6.3-4.4.0/1/build/drivers/gpu/drm/i915/intel_audio.c: At top level:
/var/lib/dkms/i915-4.6.3-4.4.0/1/build/drivers/gpu/drm/i915/intel_audio.c:746:21: warning: 
〜 略 〜

intel_audio.c内で2箇所、エラーが出力されている。こんどは、引数が少ない(too few arguments)とのことだ。

こんどは、Ubuntu 17.04のIntel GMAソースコードは大きく変わりすぎていて役立たない。この「drm/i915/dp: DP audio API changes for MST 」の記事が役に立つ。

引数を変更した新旧ソースコードの差分は次のようなものになる

$ diff -up /home/user/Desktop/修正前/intel_audio.c /usr/src/i915-4.6.3-4.4.0-1/drivers/gpu/drm/i915/intel_audio.c
--- /home/user/Desktop/修正前/intel_audio.c    2016-06-25 02:22:27.000000000 +0900
+++ /usr/src/i915-4.6.3-4.4.0-1/drivers/gpu/drm/i915/intel_audio.c    2019-06-09 18:27:48.288748045 +0900
@@ -500,6 +500,7 @@ void intel_audio_codec_enable(struct int
     struct i915_audio_component *acomp = dev_priv->audio_component;
     struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder);
     enum port port = intel_dig_port->port;
+    enum pipe pipe = -1;
 
     connector = drm_select_eld(encoder);
     if (!connector)
@@ -529,7 +530,7 @@ void intel_audio_codec_enable(struct int
     mutex_unlock(&dev_priv->av_mutex);
  
     if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify)
-        acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr, (int) port);
+            acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr, (int) port, (int) pipe);
 }
  
 /**
@@ -547,6 +548,7 @@ void intel_audio_codec_disable(struct in
     struct i915_audio_component *acomp = dev_priv->audio_component;
     struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder);
     enum port port = intel_dig_port->port;
+    enum pipe pipe = -1;
  
     if (dev_priv->display.audio_codec_disable)
         dev_priv->display.audio_codec_disable(intel_encoder);
@@ -557,7 +559,7 @@ void intel_audio_codec_disable(struct in
     mutex_unlock(&dev_priv->av_mutex);
  
     if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify)
-        acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr, (int) port);
+        acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr, (int) port, (int) pipe);
 }
  
 /**

dkmsでビルドし、インストール

/var/lib/dkms ディレクトリにある i915 モジュールの作業ディレクトリを全て削除する。

$ rm -rf /var/lib/dkms/i915*

dkmsのステータスにi915モジュールの表示が消えていることを確認したのち

$ dkms status
rtl8723bu, 4.3.6.11_12942.20141204_BTCOEX20140507-4E40, 4.4.0-148-generic, x86_64: installed
rtl8723bu, 4.3.6.11_12942.20141204_BTCOEX20140507-4E40, 4.4.0-150-generic, x86_64: installed

モジュールを登録し、ビルドする

$ dkms add i915/4.6.3-4.4.0-1
$ dkms build i915/4.6.3-4.4.0-1

ここでまたエラー発生する。モジュールのソースコードが/var/lib/dkms/i915にコピーされる一方で、dkms内のmakeでターゲットとなるのが/var/lib/dkms/i915-4.6.3-4.4.0 となってしまい、ビルドが出来ない。

安直な回避手段として、/var/lib/dkms/i915-4.6.3-4.4.0内のディレクトリは、/var/lib/dkms/i915へのシンボリックリンクとすること。

$ ln -s /var/lib/dkms/i915/4.6.3-4.4.0-1 /var/lib/dkms/i915-4.6.3-4.4.0/1

再び、ビルド、インストールを行う。

$ dkms build i915/4.6.3-4.4.0-1
Kernel preparation unnecessary for this kernel.  Skipping...
 
Building module:
cleaning build area....
make KERNELRELEASE=4.4.0-150-generic -d V=1 -C /lib/modules/4.4.0-150-generic/build M=/var/lib/dkms/i915-4.6.3-4.4.0/1/build............................
cleaning build area....
 
DKMS: build completed.
 
$ dkms install i915/4.6.3-4.4.0-1
 
i915:
Running module version sanity check.
 - Original module
 - Installation
   - Installing to /lib/modules/4.4.0-150-generic/updates/dkms/
 
intel_ips.ko:
Running module version sanity check.
 - Original module
 - Installation
   - Installing to /lib/modules/4.4.0-150-generic/updates/dkms/
 
drm_kms_helper.ko:
Running module version sanity check.
 - Original module
 - Installation
   - Installing to /lib/modules/4.4.0-150-generic/updates/dkms/
 
depmod....
 
Backing up initrd.img-4.4.0-150-generic to /boot/initrd.img-4.4.0-150-generic.old-dkms
Making new initrd.img-4.4.0-150-generic
(If next boot fails, revert to initrd.img-4.4.0-150-generic.old-dkms image)
update-initramfs........
 
DKMS: install completed.

apt --reinstall install で起動されるdkmsでもエラーがないことを再確認

現在最新のカーネル・ヘッダー パッケージ linux-headers-4.4.0-150-generic を強制再インストールして、インストールコマンドの出力を再確認する。先ほどの dkms install コマンドで、すでにモジュールがインストール済みのため、次のような「最新ファイルがあるから、処理を省略」表示であれば問題なし。

(データベースを読み込んでいます ... 現在 441319 個のファイルとディレクトリがインストールされています。)
.../linux-headers-4.4.0-150-generic_4.4.0-150.176_amd64.deb を展開する準備をしています ...
linux-headers-4.4.0-150-generic (4.4.0-150.176) で (4.4.0-150.176 に) 上書き展開しています ...
linux-headers-4.4.0-150-generic (4.4.0-150.176) を設定しています ...
/etc/kernel/header_postinst.d/dkms:
 
Good news! Module version  for i915.ko
exactly matches what is already found in kernel 4.4.0-150-generic.
DKMS will not replace this module.
You may override by specifying --force.
 
Good news! Module version  for intel_ips.ko
exactly matches what is already found in kernel 4.4.0-150-generic.
DKMS will not replace this module.
You may override by specifying --force.
 
Good news! Module version  for drm_kms_helper.ko
exactly matches what is already found in kernel 4.4.0-150-generic.
DKMS will not replace this module.
You may override by specifying --force.
initramfs-tools (0.122ubuntu8.14) のトリガを処理しています ...
update-initramfs: Generating /boot/initrd.img-4.4.0-150-generic

ブート後、新たなドライバが読み込まれているか調べる

カーネル・モジュールが、今回ビルドしたモジュールが登録されているディレクトリのものが使われていることを確認する

$ find /lib -name 'i915.ko' -print
/lib/modules/4.4.0-150-generic/updates/dkms/i915.ko    ← このモジュールが優先して読み込まれる
/lib/modules/4.4.0-150-generic/kernel/drivers/gpu/drm/i915/i915.ko
/lib/modules/4.4.0-148-generic/kernel/drivers/gpu/drm/i915/i915.ko
# modinfo i915
filename:       /lib/modules/4.4.0-150-generic/updates/dkms/i915.ko
license:        GPL and additional rights
description:    Intel Graphics
author:         Intel Corporation
author:         Tungsten Graphics, Inc.
firmware:       i915/bxt_dmc_ver1.bin
firmware:       i915/skl_dmc_ver1.bin
firmware:       i915/skl_guc_ver4.bin
srcversion:     D9DFBEAE7F50272FB94AF35
alias:          pci:v00008086d0000593Bsv*sd*bc03sc*i*
alias:          pci:v00008086d00005927sv*sd*bc03sc*i*
alias:          pci:v00008086d00005926sv*sd*bc03sc*i*
〜 略 〜

OpenGLの機能も正常に読み込まれていることを、念のために確認

$ glxinfo 
name of display: :1
display: :1  screen: 0
direct rendering: Yes
server glx vendor string: SGI
server glx version string: 1.4
server glx extensions:
    GLX_ARB_create_context, GLX_ARB_create_context_profile, 
〜 略 〜
    GLX_SGI_swap_control, GLX_SGI_video_sync
Extended renderer info (GLX_MESA_query_renderer):
    Vendor: Intel Open Source Technology Center (0x8086)
    Device: Mesa DRI Intel(R) Ironlake Desktop  (0x42)
    Version: 18.0.5
    Accelerated: yes
    Video memory: 1536MB
    Unified memory: yes
    Preferred profile: compat (0x2)
    Max core profile version: 0.0
    Max compat profile version: 2.1
    Max GLES1 profile version: 1.1
    Max GLES[23] profile version: 2.0
OpenGL vendor string: Intel Open Source Technology Center
OpenGL renderer string: Mesa DRI Intel(R) Ironlake Desktop 
OpenGL version string: 2.1 Mesa 18.0.5
OpenGL shading language version string: 1.20
OpenGL extensions:
    GL_3DFX_texture_compression_FXT1, GL_AMD_seamless_cubemap_per_texture, 
〜 略 〜